mysql 5.7 json 查询性能差因全行逐行运行时解析、无缓存与路径索引,json_extract等函数每次调用均完整解析,20kb文档单次耗时0.8–3ms,且虚拟列索引需表达式与类型完全一致才生效。

因为 MySQL 5.7 的 JSON 解析是全行、逐行、运行时完成的,不支持预编译或路径索引,大 JSON 文档会显著拖慢查询吞吐。
JSON_EXTRACT() 每次调用都触发完整解析
MySQL 5.7 不缓存 JSON 内部结构,JSON_EXTRACT(data, '$.status') 在 WHERE 或 SELECT 中每出现一次,就对整条 JSON 字符串做一次语法校验 + DOM 构建 + 路径遍历。一个 20KB 的 JSON 文档,单次解析耗时可能达 0.8–3ms——10 万行扫描就是秒级延迟。
- 无法复用已解析结果:即使同一行被多次引用(如
WHERE和SELECT都用data->>'$.name'),仍解析两次 - 无 lazy evaluation:不支持“只解析到某一层就停”,必须加载并展开整个文档树
-
JSON_CONTAINS()和JSON_SEARCH()同样全程走完整解析,不是哈希查找
虚拟列(Generated Column)不自动加速原生 JSON 表达式
你写了 WHERE data->>'$.price' = 99,哪怕已为对应虚拟列 v_price 建了索引,优化器也**不会自动重写**该语句——除非表达式字面完全一致且类型匹配。
- 常见翻车:用了
JSON_EXTRACT(data, '$.price')却建了v_price VARCHAR(20) AS (data->>'$.price')→ 类型不一致(前者返回带引号字符串,后者是去引号值),索引失效 - 若 JSON 里存的是数字
{"price": 99},必须用CAST(data->>'$.price' AS SIGNED)定义生成列,否则比较会走字符串字典序 - MySQL 5.7.8+ 才支持
->>;低于此版本只能用JSON_UNQUOTE(JSON_EXTRACT(...)),且优化器识别能力更弱
B+ 树索引根本“看不见”JSON 内部字段
JSON 列本身在磁盘上是紧凑的二进制 blob(实际是 UTF8MB4 编码的序列化结构),B+ 树只索引整列长度和前缀哈希,无法感知 $.items[0].id 这类路径是否存在、是否重复、分布是否均匀。
- 没有函数索引(MySQL 8.0.13+ 才支持
CREATE INDEX ON t((data->>'$.status'))) - 嵌套数组路径(如
$.tags[*])或动态下标($.items[?(@.active == true)])完全不可索引化 -
EXPLAIN显示type: ALL是常态,rows_examined等于表总行数,毫无悬念
真正卡住性能的往往不是 JSON 解析本身,而是你误以为“加了虚拟列就万事大吉”,却忽略了表达式一致性、类型映射和优化器重写边界——这些细节在 5.7 中比 8.x 更敏感、更难调试。











