mysql 8.0 的 json_extract 平均快 15%–35%,因其底层改用原生二进制 ast 解析器,可跳过无关字段直接定位路径,但需满足 innodb_strict_mode=on、json 字段 not null、路径无通配符等前提。

JSON_EXTRACT 在 8.0 里确实更快,但只在特定条件下成立
直接说结论:MySQL 8.0 的 JSON_EXTRACT 平均快 15%–35%,不是靠“魔法优化”,而是底层解析器彻底重写——从依赖 Boost.PropertyTree 的文本流式解析,换成原生二进制 AST 结构。这意味着它能跳过无关字段,直接定位 '$.user.address.city' 对应的内存偏移,而不是逐字节扫描整个 JSON 字符串。
但这优势有硬性前提:
-
innodb_strict_mode=ON必须开启(8.0 默认开,5.7 默认关);关掉它,8.0 会退化成类似 5.7 的松散校验逻辑,首次访问才校验,反而拖慢后续稳定读取 - JSON 字段定义需为
NOT NULL;若允许 NULL,5.7 的空值检查开销被拉高,8.0 的优化被稀释 - 路径表达式不能含通配符(如
$[*].id);5.7 根本不支持数组展开,只能全量解析+过滤,而 8.0 支持且做了短路优化——这时差距可能达 3× 以上
为什么你测不出这个速度差?常见干扰项
很多人跑完基准测试发现“8.0 更慢”或“差不多”,问题往往不在函数本身,而在没控参:
- 没统一字符集排序规则:
utf8mb4_0900_as_cs(8.0 默认)大小写敏感,utf8mb4_general_ci(5.7 常用)忽略大小写。同一句JSON_EXTRACT(data, '$.Name') = '"Admin"'在 5.7 可能命中"name": "admin",在 8.0 默认不命中——你以为结果错,其实是比较行为变了 - 没关 binlog 或调平
innodb_flush_log_at_trx_commit:5.7 默认关日志刷盘,8.0 默认开,写入压力下读取测试会被带偏 - 测试数据太小:JSON 小于 1KB 时,磁盘 I/O 和网络传输占主导,解析器差异几乎不可测;只有当 JSON 超过 10KB、嵌套深、字段多时,8.0 的跳转式解析才明显胜出
真正影响查询性能的,从来不是 JSON_EXTRACT 本身
函数再快,如果查询写成 WHERE JSON_EXTRACT(data, '$.status') = 'active' 且 data 没建索引,MySQL 就得对每行执行一次完整解析——这时瓶颈是 I/O 和内存拷贝,不是函数实现。5.7 和 8.0 都会慢,8.0 那点解析加速完全被掩盖。
正确做法(仅 8.0 支持):
- 用
ALTER TABLE t ADD COLUMN status VARCHAR(20) AS (JSON_UNQUOTE(JSON_EXTRACT(data, '$.status'))) STORED创建生成列 - 再
CREATE INDEX idx_status ON t(status) - 查询必须写成
WHERE status = 'active',写WHERE data->>'$.status' = 'active'就不走索引
5.7 只能靠冗余字段或触发器模拟,维护成本高,还容易不同步。
别忽略 JSON_PARTIAL_UPDATES 这个隐藏加速点
8.0 的 JSON_SET、JSON_REPLACE 支持就地更新(partial update),改一个 skuid 不用重写整个 JSON 文档;5.7 所有 JSON 更新都是全量 LOB 重写,高并发 UPDATE 场景下锁粒度大、I/O 压力陡增。
但注意:这个优势只在 JSON 很大(>5KB)、且更新频次高时才明显。小 JSON 下,磁盘写入延迟相同,甚至因 8.0 多了一层元数据管理略慢一点。
最易被忽略的一点:JSON 性能优势只在“用对方式”时生效——函数快 ≠ 查询快,路径索引、字符集、strict mode、partial update,缺一不可。











