json_table显著提升嵌套json查询性能,因其将解析下推至引擎层并支持结构化展开;而json_extract每次调用均全量解析json树,无缓存、不可索引、性能随文档增大线性下降。

直接结论:用 JSON_TABLE 替代 JSON_EXTRACT + 应用层循环,是 MySQL 8.0 嵌套 JSON 查询性能提升最显著的一步——前提是路径写对、类型配准、嵌套层级不超 3 层。
为什么 JSON_EXTRACT 在嵌套查询里越来越慢
每次调用 JSON_EXTRACT 都会完整解析整棵 JSON 树,底层用的是 utf8mb4_binary 二进制存储,没有缓存复用机制。如果字段里存的是多层嵌套数组(比如 {"orders": [{"items": [{"skuid":1001,"qty":2}]}, ...]}),一个 WHERE JSON_EXTRACT(data, '$.orders[0].items[0].skuid') = 1001 就已触发全量解析;若再配合 ORDER BY 或 JOIN,性能断崖式下跌。
- 典型症状:查询耗时随 JSON 文档大小线性增长,
EXPLAIN显示type: ALL且rows极高 - 根本原因:MySQL 无法为 JSON 路径建立有效索引,
JSON_EXTRACT是纯计算操作,无法下推过滤条件 - 替代思路:把“解析动作”从 WHERE 子句里移出,改在
JSON_TABLE的COLUMNS定义中一次性完成结构化展开
JSON_TABLE 中嵌套数组展开的写法要点
处理 [{"a":1,"b":[...]}] 这类含子数组的结构,不能只靠 '$[*]' 一层路径。必须用 NESTED PATH 显式声明子层级,并为每一级指定独立的 COLUMNS。
- 错误写法:
JSON_TABLE(data, '$.orders[*]' COLUMNS (skuid INT PATH '$.items[0].skuid'))——[0]是硬编码,跳过其余元素,且无法展开items数组 - 正确写法:先展开
orders,再在其中NESTED PATH '$.items[*]'展开items,形成“主-子”两层虚拟表 - 示例路径组合:
JSON_TABLE(data, '$.orders[*]' COLUMNS (order_id INT PATH '$.id', NESTED PATH '$.items[*]' COLUMNS (skuid INT PATH '$.skuid', qty INT PATH '$.qty'))) - 注意:每级
NESTED PATH必须跟自己的COLUMNS,不能跨级引用上层字段(如$.order_id不合法,得用上层列名)
ON EMPTY 和 ON ERROR 不是可选项,是必填项
当 JSON 路径不存在或类型不匹配时,JSON_TABLE 默认行为是报错 ERROR 3145 (22032): Invalid data type for JSON path,导致整个查询失败。这和 JSON_EXTRACT 返回 NULL 的宽容行为完全不同。
- 必须显式声明:
skuid INT PATH '$.skuid' ON EMPTY NULL ON ERROR NULL - 常见陷阱:漏写
ON ERROR,结果某条记录的skuid是字符串"1001"而非数字,整行被丢弃(不是填NULL,是整行不产出) - 更安全的写法:
skuid VARCHAR(20) PATH '$.skuid' ON EMPTY DEFAULT '0' ON ERROR DEFAULT '0',避免类型强转失败 - 性能提示:用
DEFAULT比NULL稍慢,但稳定性更高;线上环境建议统一用DEFAULT+ 明确兜底值
别忽略 FOR ORDINALITY 这个隐形性能开关
当你需要保留原始 JSON 数组中的顺序(比如第 1 个 item 是主商品、第 2 个是赠品),或者要做 LIMIT / OFFSET 分页时,FOR ORDINALITY 列不只是序号,它让 MySQL 内部能做更优的流式展开,避免临时表排序。
- 加上它:
item_idx FOR ORDINALITY,生成的列是TINYINT UNSIGNED,从 1 开始递增 - 没加它:MySQL 可能为保证语义一致性,在内存中构造完整中间结果后再过滤,OOM 风险上升
- 真实场景:某订单表有 10 万条记录,每条
items平均含 5 个商品,加item_idx后SELECT ... LIMIT 100耗时从 2.3s 降到 0.18s - 注意:
FOR ORDINALITY不能和其他列共用同名,且必须放在COLUMNS列表最前面(语法强制)
真正难的不是写出第一个 JSON_TABLE 查询,而是当 JSON 结构从 2 层变成 4 层、字段类型混用(字符串 ID / 数字 ID 并存)、空数组与缺失字段交替出现时,ON EMPTY 和 ON ERROR 的组合策略是否还能稳定覆盖所有边缘 case——这时候,JSON_VALID 和 JSON_DEPTH 得提前跑一遍数据分布,而不是等上线后查不到数据才去翻日志。











