函数索引生效需同时满足MySQL≥8.0.13、索引表达式用双括号(( ))包裹、WHERE条件与索引表达式逐字一致;否则全表扫描。

直接建函数索引能生效,但必须满足三个硬性条件:MySQL ≥ 8.0.13、索引表达式带双括号、WHERE条件与索引表达式完全一致;漏掉任一条件,查询照样全表扫描。
函数索引语法必须写对:少一个括号就报错
MySQL 对函数索引的括号语法极其敏感,外层那对 (()) 是强制要求,不是可选格式。它用来明确告诉优化器:“这是一个计算表达式,不是普通列名”。
-
CREATE INDEX idx_type ON goods ((details->>'$.type'))✅ 正确:两层括号包裹表达式 -
CREATE INDEX idx_type ON goods (details->>'$.type')❌ 报错:ERROR 1064 或 ERROR 3105 -
CREATE INDEX idx_price ON goods ((CAST(details->>'$.price' AS DECIMAL(10,2))))✅ 即使用了CAST,也必须套两层括号
别指望 MySQL 自动容错——它不解析单层括号里的内容,直接拒绝建索引。
WHERE 条件必须和索引表达式逐字一致
函数索引不会做隐式等价推导。哪怕只是多一个空格、大小写不同、或用了等效函数,索引就失效。
- 索引是
((details->>'$.type')),查询必须写WHERE details->>'$.type' = '1' - 写成
WHERE JSON_EXTRACT(details, '$.type') = '1'→ 不走索引 - 写成
WHERE CAST(details->>'$.type' AS UNSIGNED) = 1→ 不走索引(类型转换和表达式都变了) - 写成
WHERE details ->> '$.type' = '1'(中间加空格)→ 在部分 8.0.13–8.0.16 版本中会失效
验证是否命中:执行 EXPLAIN SELECT * FROM goods WHERE details->>'$.type' = '1',看 key 列是否显示索引名,type 是否为 ref 或 range。
类型匹配比路径正确更重要
JSON 提取结果默认是 TEXT 类型,而 TEXT 无法参与标准 B+ 树索引比较。不显式转类型,索引建了也白建。
- 数字字段(如
$.price,$.user_level):用CAST(... AS UNSIGNED)或CAST(... AS DECIMAL(10,2)) - 字符串字段(如
$.email,$.city):用CAST(... AS CHAR(100)),别用TEXT,也别省略长度 - 日期字段(如
$.created_at):用CAST(... AS DATETIME),避免和字符串隐式比较 - 数组元素(如
$.tags[0]):函数索引不支持[*]多值展开,只能固定下标;要查整个数组内容,得用生成列 +MEMBER OF()配合多值索引
常见坑:提取 user_id 是数字,却用 CHAR(50) 建索引,导致数值比较时隐式转换,索引失效。
函数索引不是万能替代,复杂场景仍需生成列
函数索引轻量,但能力边界清晰。以下情况建议改用 STORED 生成列 + 普通索引:
- 需要处理
ON EMPTY或ON ERROR(比如 JSON 缺失字段时设默认值),函数索引不支持这类行为控制 - 要对
$.tags[*]这类多值路径建索引(8.0.17+ 才支持函数索引多值,旧版只能靠生成列 +JSON_TABLE或MULTISET) - WHERE 条件里混用多个 JSON 路径(如同时查
type和status),函数索引只能单点加速,生成列可建联合索引 - 写入频繁且字段值变化大,
STORED列值物理存储,更新时重算一次;函数索引虽不存值,但优化器在某些小版本(如 8.0.14)有已知匹配失效 case
真正容易被忽略的点是:函数索引只解决“查得快”,不解决“写得稳”——它对数据质量零干预,而生成列配合 NOT NULL 和默认值,能从源头过滤脏数据。


















