json字段where查询慢是因为无法使用b+树索引,需全表扫描并解析json;应通过虚拟列(stored)配合->>提取值并建索引,或mysql 8.0.13+使用函数索引create index idx_name on t1 ((json_col->>'$.name'))。

JSON字段直接WHERE查询为什么慢
MySQL对JSON字段执行WHERE json_col->'$.name'这类查询时,无法利用B+树索引,每次都要全表扫描并解析整个JSON文档——哪怕只查一个字段。这是性能瓶颈的根源,不是配置能绕开的。
用虚拟列把JSON路径“固化”成普通字段
虚拟列(VIRTUAL)不占用存储空间,但能被索引。关键是要用JSON_EXTRACT()或更简洁的->>操作符提取值,并确保类型匹配:
-
name_strvarchar(64) GENERATED ALWAYS AS (json_col->>'$.name') STORED - 必须加
STORED才能建索引(MySQL 5.7+要求函数索引字段为STORED) - 类型要和实际JSON值一致:字符串用
varchar,数字用decimal或unsigned int,否则隐式转换会导致索引失效 - 避免用
JSON_EXTRACT(json_col, '$.name')——它返回带引号的JSON字符串,而->>自动去引号,更符合业务查询习惯
给虚拟列建函数索引要注意什么
MySQL 8.0.13+ 支持在虚拟列上建普通B+树索引,但若想直接对JSON路径建索引(不显式建虚拟列),需用函数索引:
- 语法:
CREATE INDEX idx_name ON t1 ((json_col->>'$.name'));——注意双括号,这是函数索引的必需写法 - 函数索引字段必须是确定性表达式,
->>满足,JSON_EXTRACT()也满足,但NOW()或RAND()不行 - 索引长度限制仍适用:
varchar(255)字段建索引默认只索引前767字节(InnoDB),超长需用innodb_large_prefix=ON并调大innodb_page_size - EXPLAIN时看到
key: idx_name且rows显著下降,才算生效;如果type: ALL,大概率是类型不匹配或用了->而非->>
哪些场景不适合JSON字段加索引
不是所有JSON查询都适合走索引优化:
- 频繁更新JSON整体内容(如
UPDATE t1 SET json_col = JSON_SET(json_col, '$.status', 'done'))会触发虚拟列重算,增加写开销 - 查询条件含通配符或正则,比如
WHERE json_col->>'$.desc' LIKE '%error%',即使有索引也无法范围扫描,仍是全表扫描+过滤 - 嵌套过深或路径动态拼接(如
CONCAT('$.tag_', user_id)),MySQL无法在预编译阶段确定路径,函数索引无效 - 单表数据量小于10万行,且QPS不高,加索引反而增加维护成本,不如应用层缓存或冗余字段
真正需要关注的是那些高频、等值、路径固定的查询——比如按用户配置中的theme或region_id筛选,这类才值得投入虚拟列+索引。











