函数索引生效需同时满足:MySQL ≥ 8.0.13、索引表达式用双括号语法、WHERE条件与索引表达式逐字完全一致;否则因语法错误、类型不匹配或表达式差异导致索引失效。

直接建函数索引能生效,但必须满足三个硬性条件:MySQL ≥ 8.0.13、索引表达式带双括号、WHERE条件与索引表达式完全一致。否则哪怕只差一个空格或类型隐式转换,索引就彻底失效。
CREATE INDEX 必须用双括号语法,少一个就报错
MySQL 对函数索引的语法极其敏感,外层那对 (()) 是强制要求,不是可选项。
-
CREATE INDEX idx_user_level ON orders ((extra->>'$.user_level'))✅ 正确 -
CREATE INDEX idx_user_level ON orders (extra->>'$.user_level')❌ 报 ERROR 1064 或 ERROR 3105 -
CREATE INDEX idx_price ON goods ((CAST(details->>'$.price' AS DECIMAL(10,2))))✅ 带 CAST 也要套两层括号
漏掉外层括号时,MySQL 会当成普通列索引处理,而 JSON 列本身不支持直接索引,所以建表语句直接失败。
WHERE 条件必须和索引表达式逐字匹配
优化器不会做任何表达式等价推导——它只做字符串级比对。哪怕只是多一个空格、大小写不同、或用了等效函数,索引就无法命中。
- 如果索引是
((extra->>'$.user_level')),查询必须写成WHERE extra->>'$.user_level' = '3' - 写成
WHERE JSON_UNQUOTE(JSON_EXTRACT(extra, '$.user_level')) = '3'❌ 不走索引 - 写成
WHERE extra ->> '$.user_level' = '3'(中间多空格)❌ 多数版本也不走 - 写成
WHERE CAST(extra->>'$.user_level' AS UNSIGNED) = 3❌ 类型和索引不一致,且表达式已变更
验证是否命中:执行 EXPLAIN SELECT * FROM orders WHERE extra->>'$.user_level' = '3',看 key 列是否显示索引名,type 是否为 ref 或 range。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
类型必须显式声明,避免隐式转换导致失效
JSON 提取结果默认是 TEXT 类型,而 TEXT 不能直接参与 B+ 树比较;若字段实际存的是数字或日期,不转类型会导致索引失效或结果错误。
- 数字字段(如
$.score):用CAST(... AS UNSIGNED)或CAST(... AS SIGNED) - 字符串字段(如
$.city):用CAST(... AS CHAR(64)),别用TEXT - 日期字段(如
$.created_at):用CAST(... AS DATETIME),否则和字符串比较会出错 - 路径中含数组(如
$.tags[0]):仍按字符串处理,用CHAR(100)即可
常见坑:用户等级字段存的是数字 3,但索引建的是 ((extra->>'$.user_level'))(返回字符串),而查询写成 = 3(整数),MySQL 自动转类型,索引失效。
函数索引不支持多值展开和错误兜底
如果你需要查 JSON 数组里的任意元素(如 $.tags[*]),或者希望提取失败时返回默认值,函数索引完全无能为力。
-
$.tags[*]这种路径在函数索引里不被支持,8.0.17 前只能靠STORED生成列 + 多值索引 - 函数索引遇到
NULL、路径不存在、类型不合法等情况,查询直接报错或返回空,无法设DEFAULT或ON EMPTY - 写入性能上,函数索引虽不存物理值,但 8.0.13–8.0.16 存在已知的索引失效 case,线上环境建议至少用 8.0.17+
真正要稳定支撑复杂 JSON 查询逻辑的场景,STORED 生成列仍是更可控的选择——函数索引轻量,但边界清晰;一旦越界,就得切方案。










