mysql 8.0.17+才支持多值索引,需用cast转为array类型并配合member of或json_contains查询,不支持排序/范围扫描,低版本会报错3152。

MySQL 8.0.17+ 才支持多值索引,低于这个版本直接报错
多值索引是 MySQL 8.0.17 引入的特性,依赖 JSON_CONTAINS 和 MEMBER OF 等函数,底层靠虚拟生成列 + 函数索引实现。如果你用的是 8.0.16 或更早版本,执行 CREATE INDEX ... ON ... (col->"$.tags") 会报错 ERROR 3152 (HY000): Cannot create a functional index on a JSON column —— 这不是语法写错了,是版本不支持。
确认版本:运行 SELECT VERSION();,输出必须 ≥ 8.0.17;生产环境升级前务必验证备份与复制兼容性,尤其主从版本混搭时,从库低于 8.0.17 会导致 SQL thread 报错中断。
JSON 字段必须先提取数组,再用 CAST(... AS UNSIGNED ARRAY) 或 CAST(... AS CHAR(255) ARRAY)
多值索引不能直接建在 json_col->"$.tags" 上,必须显式转换为数组类型。常见错误是漏掉 CAST 或类型不匹配:
-
tags是字符串数组(如["apple","banana"])→ 必须用CAST(json_col->"$.tags" AS CHAR(255) ARRAY) -
tags是数字数组(如[1,2,3])→ 必须用CAST(json_col->"$.tags" AS UNSIGNED ARRAY),用CHAR会索引失效 - 如果数组元素类型混杂(比如
["a", 1]),MySQL 无法隐式转成统一类型,建索引会失败
正确示例:
ALTER TABLE products ADD COLUMN tags_array VARCHAR(255) GENERATED ALWAYS AS (CAST(data->"$.tags" AS CHAR(255) ARRAY)) STORED;
CREATE INDEX idx_tags ON products (tags_array);
查询时必须用 MEMBER OF 或 JSON_CONTAINS,不能用 = 或 IN
即使建好了多值索引,写错查询条件照样走全表扫描。MySQL 只有在谓词明确匹配多值语义时才会下推索引:
- ✅ 正确(走索引):
WHERE 'apple' MEMBER OF (data->"$.tags")或WHERE JSON_CONTAINS(data->"$.tags", '"apple"') - ❌ 错误(不走索引):
WHERE data->"$.tags" = '["apple"]'、WHERE 'apple' IN (data->"$.tags")、WHERE data->"$.tags" LIKE '%apple%' - 注意
JSON_CONTAINS第二个参数必须是 JSON 字符串(带双引号),写成JSON_CONTAINS(data->"$.tags", 'apple')会返回 NULL,查不到数据
多值索引不支持排序和范围扫描,只加速存在性判断
它本质是把 JSON 数组每个元素映射为独立索引项,类似“展开后建普通 B+ 树索引”。所以:
- 支持高效查“是否包含某值”,但不支持
ORDER BY data->"$.tags"或WHERE data->"$.tags" > 'z' - 无法利用索引做
COUNT(DISTINCT ...)优化,聚合仍需扫描 - 单条记录含大量数组元素(比如 1000 个 tag)会显著增大索引体积,写入性能下降,建议业务侧限制数组长度
真正需要排序或范围查询的场景,别硬套多值索引——拆成关联表更稳。










