多值索引是将JSON数组的每个元素作为独立索引项存入B+树,需用CAST(... AS ... ARRAY)声明,仅MEMBER OF/JSON_CONTAINS/JSON_OVERLAPS可触发,不适用于普通等值或IN查询。

多值索引不是“给JSON字段建索引”,而是给数组元素建索引
MySQL 的 B+ 树索引天然不认 JSON 表达式,data->'$.tags' 或 data->>'$.tags' 都是运行时函数调用,优化器无法将其映射到物理索引结构。多值索引绕开了这个限制:它不索引整个 JSON 字段,也不索引某个路径的标量结果,而是把 JSON 数组里的**每个独立元素**,作为一条单独的索引记录存入 B+ 树。
比如 {"user": "Alice", "roles": ["admin", "editor"]} 这条记录,在多值索引 (CAST(data->'$.roles' AS CHAR(32) ARRAY)) 下,实际会生成两条索引项:"admin" → 指向该行,"editor" → 指向该行。查询时只要命中任一元素,就能快速定位到整行数据。
必须用 CAST(... AS ... ARRAY),且类型要和数组元素严格匹配
多值索引本质是函数索引的一种特殊形态,语法上强制要求使用 CAST() 并显式声明 ARRAY 类型。漏掉 ARRAY、类型不匹配、或用了 ->(返回 JSON 类型)而非 ->'(返回标量),都会导致索引创建失败或失效。
-
CAST(data->'$.zipcode' AS UNSIGNED ARRAY)✅ 正确:JSON 数组 → 无符号整数数组 -
CAST(data->>'$.zipcode' AS UNSIGNED ARRAY)❌ 错误:->>返回字符串,无法 CAST 成UNSIGNED ARRAY -
CAST(data->'$.tags' AS CHAR(64))❌ 错误:缺ARRAY,这不是多值索引,只是普通函数索引(且对数组无效) -
CAST(data->'$.tags' AS JSON ARRAY)❌ 错误:MySQL 不支持JSON ARRAY,只支持标量类型 +ARRAY(如CHAR、INT、DATETIME)
只有 MEMBER OF / JSON_CONTAINS / JSON_OVERLAPS 能触发多值索引
多值索引不会被普通的等值查询或 IN 使用。它的生效条件非常明确,仅限以下三种谓词:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
'admin' MEMBER OF (data->'$.roles')JSON_CONTAINS(data->'$.roles', '"admin"')JSON_OVERLAPS(data->'$.roles', '["admin", "guest"]')
写成 WHERE data->>'$.roles' = '["admin","editor"]' 或 WHERE '"admin"' IN (data->>'$.roles') 都不会走多值索引——前者查的是整个数组字符串,后者语法非法且语义错误。
STORED 虚拟列 + 普通索引仍是更通用的选择
多值索引只解决一个子问题:JSON 数组中「任意元素匹配」的场景。但很多业务需要的是「数组中第 N 个元素」或「嵌套对象字段」,比如 data->>'$.items[0].price' 或 data->>'$.meta.created_by'。这些仍得靠 STORED 虚拟列 + 普通索引。
而且多值索引有隐含成本:每条记录的数组有多少元素,就产生多少索引记录。一个含 100 个标签的用户,就会在索引里占 100 行。如果数组长度不可控,索引体积和维护开销会显著上升。
真正容易被忽略的点是:多值索引和虚拟列索引不是互斥方案,而是互补。你完全可以同时存在 idx_roles_multi(加速 MEMBER OF)和 idx_first_role(加速 data->>'$.roles[0]'),只要表达式路径和类型定义都干净、确定。

















