MySQL 8.0函数索引需双层括号、CAST类型须匹配查询值类型,数组路径须用ARRAY多值索引,固定路径推荐STORED生成列+索引。

MySQL 8.0 中可以直接对 JSON 路径建函数索引,但语法、类型和括号层级稍有不慎就会报错或失效——不是加了 INDEX 就能走索引。
函数索引语法必须带双层括号
MySQL 8.0+ 的函数索引要求索引定义中显式包裹表达式,外层括号不能省略。少一个括号,语句直接报错 ERROR 1064;多一个或位置错,可能静默创建失败。
-
ALTER TABLE t ADD INDEX idx_email((CAST(properties->>'$.request.email' AS CHAR(100))));✅ 正确:最外层两个括号((...)) -
ALTER TABLE t ADD INDEX idx_email(CAST(properties->>'$.request.email' AS CHAR(100)));❌ 错误:只有一层括号,语法不识别 -
ALTER TABLE t ADD INDEX idx_email((properties->>'$.request.email'));❌ 危险:没CAST,返回类型是JSON,而JSON类型不支持索引,实际建的是无效索引
CAST 类型必须匹配查询条件的值类型
索引字段的类型决定能否命中等值查询。如果业务中用 WHERE config->>'$.status' = 'active',但索引用了 CAST(... AS UNSIGNED),就会因隐式转换导致索引失效。
- 字符串路径(如 email、name)→ 用
CHAR(N)或VARCHAR(N),N 建议 ≥ 实际最大长度 - 数字路径(如
$.user_id)→ 用UNSIGNED或BIGINT,避免CAST(... AS CHAR)后做数值比较 - 布尔值 → MySQL 存 JSON 布尔时是
true/false字符串,建议统一转成TINYINT:CAST(config->>'$.enabled' AS TINYINT) - 日期路径(如
$.created_at)→ 用DATETIME,且确保 JSON 中存的是 ISO 格式字符串("2026-09-28T14:30:00")
数组路径需用多值索引(MySQL 8.0.17+)
查 config->'$[*].tag' 这类展开数组的场景,普通函数索引只取第一个元素,无法覆盖全部。必须用多值索引,否则 WHERE JSON_CONTAINS(config, '"urgent"', '$.tags') 仍会全表扫描。
- 建法示例:
ALTER TABLE t ADD INDEX idx_tags((CAST(config->'$[*].tag' AS CHAR(32) ARRAY))); - 注意:
ARRAY关键字必须写在CAST内部,且类型要与数组元素一致(如全是字符串就用CHAR,全是数字就用UNSIGNED ARRAY) - 仅适用于
JSON_CONTAINS()或MEMBER OF()查询,->>取值后等值匹配不走该索引 - 8.0.17 之前版本不支持,强行执行会报错
ERROR 3135
别忽略生成列的兜底价值
函数索引虽灵活,但不支持动态路径(比如想同时按 $.items[0].id 和 $.items[1].id 查),也不支持带变量的路径表达式。这时候生成列仍是更稳的选择:
ALTER TABLE t ADD COLUMN first_item_id INT GENERATED ALWAYS AS (config->>'$.items[0].id') STORED;CREATE INDEX idx_first_item ON t(first_item_id);- 只要路径固定、表达式确定,STORED 列 + 索引就是最可靠、兼容性最好的方案
- 别用
VIRTUAL列建索引——MySQL 不允许
真正容易被忽略的是:函数索引一旦创建,其 CAST 类型和路径就固化了;改路径得删索引重建,而生产环境 DDL 可能锁表。不如提前评估好高频查询路径,再决定用函数索引还是生成列。


















