mysql 5.7 json更新需重写整字段,引发行锁争用与binlog膨胀;8.0支持部分更新、函数索引替代stored列,并强化utf-8校验,解析开销增大但写入效率显著提升。

MySQL 5.7 和 8.0 的 JSON 类型存储效率差异,核心不在“存得快不快”,而在于“怎么存”和“改的时候动多少数据”。5.7 存的是优化过的二进制结构,但每次更新都重写整段 JSON;8.0 在此基础上加了真正的部分更新能力,只改路径对应字段,I/O 和锁开销大幅降低。
JSON 存储格式本身没变,但解析和写入路径变了
两个版本都用二进制格式(不是纯文本)存 JSON,这点一致。真正影响效率的是底层处理逻辑:
- 5.7 的
JSON_SET()、JSON_REPLACE()看似局部操作,实际仍触发整行重写——InnoDB 必须把整个 JSON 字段的二进制 blob 读出、修改、再完整写回 - 8.0.27+ 引入
JSON_PARTIAL_UPDATE机制,配合优化后的JSON_SET(),能定位到内部节点直接 patch,避免全量落盘 - 这意味着:高并发更新同一张表的 JSON 字段时,5.7 更容易因大字段重写引发行锁争用、binlog 膨胀、主从延迟
STORED 生成列带来额外写放大(5.7 独有)
为加速查询,5.7 强制依赖 STORED 生成列 + 索引。但这不是免费的:
- 每次更新原始 JSON 字段,MySQL 必须同步重算所有相关
STORED列,并将它们一并写入磁盘 - 比如一个
profileJSON 字段上建了 3 个STORED列(user_id、status、region),哪怕只改$.status,三个列都要参与落盘 - 8.0 用函数索引替代该方案,索引项只存表达式结果,不新增物理列,无此写放大
字符集校验与解析开销在 8.0 中更重
别被“二进制存储”误导——解析阶段的代价不可忽略:
- 8.0 对 JSON 内部字符串默认启用更严格的
utf8mb4_0900_as_cs校验,尤其在JSON_CONTAINS()或带比较的路径查询中,每个元素都要做完整 Unicode 归一化 - 5.7 的校验路径更轻量,但代价是可能放过某些边缘非法编码(如非最小化 UTF-8)
- 这个差异在无索引的全表 JSON 扫描中尤为明显:同样 10 万行,8.0 解析耗时可能高出 30%~50%,除非你已建好对应索引
真正影响生产效率的,往往不是单次 INSERT 的毫秒差,而是高写入场景下锁粒度、binlog 体积、备库回放压力这些隐性成本。升级前务必用真实负载压测写路径,别只看 SELECT 性能报告。











