多值索引仅响应 member of、json_contains、json_overlaps 三类谓词,因其实质是将 json 数组各元素拆为独立索引项存入 b+ 树;where data->>'$.tags' = '["admin"]' 不走索引,因其返回字符串并需运行时解析,优化器无法识别数组结构,只能全表扫描。

多值索引只对 MEMBER OF、JSON_CONTAINS、JSON_OVERLAPS 有效,其他写法哪怕语义等价也完全不走索引。
为什么WHERE data->>'$.tags' = '["admin"]'不走多值索引
MySQL 的 B+ 树索引无法识别运行时 JSON 解析表达式。data->>'$.tags' 是函数调用,每次执行都要解析整个 JSON 字符串并去除引号,优化器看不到“结构”,只能全表扫描(EXPLAIN 显示 type: ALL)。多值索引不是给字段建索引,而是把数组每个元素拆成独立索引项——它根本不响应这种字符串级的等值比较。
常见错误现象:
-
WHERE '"admin"' IN (data->>'$.tags'):语法非法,MySQL 报错ERROR 3143 -
WHERE data->>'$.tags' LIKE '%admin%':全文扫描 + 字符串匹配,索引失效 -
WHERE JSON_EXTRACT(data, '$.tags') = '["admin"]':同->>,纯计算,不走索引
CAST(... AS ... ARRAY) 的类型和括号一个都不能错
多值索引本质是带 ARRAY 修饰的函数索引,语法硬性要求严格:
- 必须用
->(返回 JSON 类型),不能用->>(返回字符串);CAST(data->>'$.tags' AS CHAR(32) ARRAY)❌ 直接报错 - 类型必须和数组元素一致:
CAST(data->'$.zipcode' AS UNSIGNED ARRAY)✅;CAST(data->'$.zipcode' AS CHAR(10) ARRAY)❌(若实际是数字,隐式转换废索引) - 必须显式写
ARRAY;CAST(data->'$.tags' AS CHAR(32))❌ 这只是普通函数索引,对数组无效 - 外层双括号不能少:
ADD INDEX idx_tags ((CAST(data->'$.tags' AS CHAR(32) ARRAY)))✅;少一层括号就ERROR 3105
哪些查询能真正触发多值索引
只有三个谓词能命中,且右侧值格式必须匹配数组元素类型:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
-
'admin' MEMBER OF (data->'$.roles'):左侧是标量,右侧是 JSON 路径,元素类型需与索引声明一致 -
JSON_CONTAINS(data->'$.roles', '"admin"'):第二个参数必须是 JSON 字符串(带引号),'admin'或JSON_ARRAY('admin')都不行 -
JSON_OVERLAPS(data->'$.roles', '["admin", "guest"]'):右侧必须是合法 JSON 数组字符串
性能影响明显:百万级数据下,上述任一写法可将查询从秒级降至毫秒级;但若误写为 JSON_CONTAINS(data, '"admin"')(漏路径),则退化为全表扫描。
比多值索引更通用的替代方案
多值索引只解决「数组中任意元素匹配」这一个场景。一旦业务需要查「第 N 个元素」或「嵌套对象字段」,比如 data->>'$.items[0].price',它就完全无能为力。
此时应优先考虑:
- 用
STORED虚拟列 + 普通索引:例如ADD COLUMN tag0 VARCHAR(64) GENERATED ALWAYS AS (data->>'$.tags[0]') STORED,再建INDEX - 复杂嵌套用
JSON_TABLE:配合NESTED PATH展开多层结构,再在结果集上加索引或过滤 - 高频固定路径,直接建函数索引:如
CREATE INDEX idx_price ON t ((CAST(data->>'$.price' AS DECIMAL(10,2))))
最容易被忽略的一点:多值索引无法支持 ORDER BY 或 GROUP BY 加速,它的定位非常垂直——仅加速「是否存在某元素」这类布尔判断。










