json字段where查询慢因每次解析全文且无法索引;需用stored生成列提取路径值并建索引,如user_id列+idx_user_id索引,查询改where user_id=123即可走索引。

JSON字段直接WHERE查询为什么慢
MySQL对JSON字段做WHERE json_col->'$.name'这类查询时,每次都要解析整个JSON文档,无法利用B+树索引——本质上是全表扫描。哪怕加了普通索引也没用,因为JSON类型不支持直接索引。
用虚拟生成列把JSON路径“抽出来”再建索引
核心思路是把经常查的JSON子值,用GENERATED ALWAYS AS固化成一个普通列,再在它上面建索引。这个列不占额外存储(VIRTUAL模式),只在查询时按需计算。
- 假设表
t有data JSON字段,常查data->>'$.user_id' - 添加虚拟列:
ALTER TABLE t ADD COLUMN user_id INT GENERATED ALWAYS AS (data->>'$.user_id') STORED;
(注意:必须用STORED才能建索引;VIRTUAL不行) - 再建索引:
CREATE INDEX idx_user_id ON t(user_id);
- 之后查询写成
WHERE user_id = 123,就能走索引了
前缀索引对JSON_EXTRACT结果无效,但对生成列有效
JSON_EXTRACT()或->操作符返回的是JSON类型,不能直接加前缀索引;但生成列可以是VARCHAR,这时前缀索引就有意义。
- 比如要查
data->>'$.title'且标题可能很长,可定义:ADD COLUMN title VARCHAR(255) GENERATED ALWAYS AS (data->>'$.title') STORED
- 然后建前缀索引:
CREATE INDEX idx_title_prefix ON t(title(10));
- 注意:只有
WHERE title LIKE 'abc%'这类前缀匹配才生效;=或IN仍建议用完整长度索引 - 别对
JSON字段本身建前缀索引,如INDEX(data(10)),MySQL会报错或静默忽略
生成列的表达式必须确定、无副作用
MySQL要求生成列表达式是“确定性”的,否则建表或加列会失败。
- 允许:
data->>'$.status'、JSON_UNQUOTE(JSON_EXTRACT(data, '$.id')) - 禁止:
NOW()、RAND()、UUID()、子查询、用户变量 - 嵌套JSON路径要小心:如果
$.items是数组,data->>'$.items[0].name'在某些版本可能不被允许(5.7.13+ 支持,但需确认) - 类型转换要显式:
CAST(data->>'$.score' AS SIGNED)比直接data->>'$.score'更安全,避免隐式转换导致索引失效











