mysql中json字段无法直接索引,需通过5.7的stored生成列或8.0+的函数索引实现高效查询;全表扫描因json路径表达式不可索引导致,且生成列表达式须确定、类型需匹配。

直接 WHERE json_col->>'$.key' 会全表扫描
MySQL 对 json_col->>'$.key' 这类表达式无法走索引,每次查询都要解析整段 JSON 字符串,本质是 type: ALL 全表扫描。哪怕你给 json_col 加了普通索引或前缀索引(比如 INDEX(json_col(10))),MySQL 也会静默忽略或报错——JSON 类型不支持直接索引。
MySQL 5.7 必须用 STORED 生成列 + 索引
虚拟列(VIRTUAL)不能建索引,必须用 STORED 模式:
ALTER TABLE t ADD COLUMN user_id INT GENERATED ALWAYS AS (data->>'$.user_id') STORED;CREATE INDEX idx_user_id ON t(user_id);- 之后查
WHERE user_id = 123才能真正走 B+ 树索引 - 表达式必须确定:禁止
RAND()、NOW()、子查询;允许JSON_UNQUOTE(JSON_EXTRACT(data, '$.id')) - 嵌套数组如
data->>'$.items[0].name'在 5.7.13+ 支持,但生产环境建议先验证版本
MySQL 8.0+ 可用函数索引,但注意 CAST 和双括号
不用改表结构,直接在 JSON 路径上建索引,但语法严格:
ALTER TABLE t ADD INDEX idx_email((CAST(properties->>'$.request.email' AS CHAR(100))));- 注意外层两个括号:
((...)),少一个就会语法错误 - 类型要匹配:数字用
UNSIGNED,日期用DATETIME,避免隐式转换导致索引失效 - 多值索引(数组)需用
ARRAY类型:例如CAST(data->'$[*].tag' AS CHAR(32) ARRAY)
前缀索引只对 VARCHAR/TEXT 生成列有效
JSON_EXTRACT() 或 ->> 返回的是 JSON 类型,不能加前缀索引;但生成列为 VARCHAR 就可以:
ADD COLUMN title VARCHAR(500) GENERATED ALWAYS AS (data->>'$.title') STORED;CREATE INDEX idx_title_prefix ON t(title(10));- 这个索引只对
WHERE title LIKE 'abc%'有效;等值查询=建议用完整长度索引 - 别写
INDEX(data(10))—— 无效且可能掩盖问题
$.items[2].name 或动态下标,就得新增列或重构;而函数索引虽灵活,但 8.0.17 之前不支持多值,数组场景仍得靠生成列兜底。











