部分更新 怎样减少 JSON 写放大
来源:17golang原创
时间:2026-10-25 10:58:51 231浏览 收藏
JSON 列里只改一个字段时,不要先在应用层读取整份文档、改完再把完整 JSON 写回去。更合适的做法是让 UPDATE 直接使用 JSON_SET()、JSON_REPLACE() 或 JSON_REMOVE(),并保证输入列和目标列是同一个 JSON 列。这样 MySQL 才有机会对二进制 JSON 做原地部分更新,减少存储层的写放大;如果使用行格式复制,再配合 binlog_row_value_options=PARTIAL_JSON,也可以减少副本日志中的 JSON after-image。
JSON_SET()既能替换已有路径,也能添加新路径;只替换已有值时可考虑JSON_REPLACE()。- 部分更新的关键是“同一 JSON 列作为输入和目标”,直接赋整段字符串会退化为完整替换。
JSON_STORAGE_FREE()可辅助判断列内是否出现可复用的空闲空间,但它不是性能基准。
先把整段回写改成路径更新
假设订单表的 attributes 是 JSON 列,现在只想把配送偏好改成 express。下面两种语句结果可能相同,但写入路径不同:
-- 只更新 attributes 中的 delivery.speed,输入列和目标列保持一致 UPDATE orders SET attributes = JSON_SET(attributes, '$.delivery.speed', 'express') WHERE id = 1001; -- 只在路径已经存在时替换;路径不存在时不会新增字段 UPDATE orders SET attributes = JSON_REPLACE(attributes, '$.delivery.speed', 'express') WHERE id = 1001; -- 删除一个路径,避免把剩余键重新拼成完整 JSON UPDATE orders SET attributes = JSON_REMOVE(attributes, '$.delivery.legacy_code') WHERE id = 1001;
JSON_SET() 的路径和值按从左到右处理,因此一次语句里多个路径要注意前一个修改会成为后一个路径的输入。需要“字段不存在就不动”的语义时,用 JSON_REPLACE();需要删除字段时使用 JSON_REMOVE()。这些函数只解决 JSON 文档内部的局部修改,不会替你判断业务字段是否允许变更。

让 MySQL 保留原地部分更新条件
MySQL 官方文档给出的核心条件有三个:目标列确实声明为 JSON;更新表达式使用上述三个修改函数;输入列和目标列必须是同一列。例如 attributes = JSON_SET(attributes, ...) 满足这个方向,而把 profile 的内容计算后写入 attributes,即使结果只改变一个键,也不能按同样方式利用该优化。
因此,下面这种“从应用层带回完整文档再赋值”的写法不要作为局部更新方案:
-- 这是完整替换,不是 JSON 列的路径级部分更新
UPDATE orders
SET attributes = '{"delivery":{"speed":"express"},"coupon":"NEW"}'
WHERE id = 1001;
如果业务一次确实要重建整个文档,完整替换并不错误,只是不要把它误认为减少 JSON 写放大的路径。尤其是复杂数组的大范围重排、跨字段迁移或 schema 重建,局部函数不一定能带来同等收益。
用 JSON_STORAGE_FREE 做低成本检查
更新后可以查看 JSON 二进制表示中当前可复用的空闲空间。这个指标适合检查“是否可能发生了原地更新”,不适合直接当作吞吐量或延迟结论:
-- 查看这一行 JSON 文档的占用和可复用空间 SELECT JSON_STORAGE_SIZE(attributes) AS stored_bytes, JSON_STORAGE_FREE(attributes) AS free_bytes FROM orders WHERE id = 1001;
用 JSON_SET()、JSON_REPLACE() 或 JSON_REMOVE() 缩短某个值后,JSON_STORAGE_FREE() 可能变大;直接给 JSON 列赋新文档,或更新不能利用原地优化时,通常会看到 0。结果还会受到新值大小、列布局和更新表达式的影响,所以要在代表性数据上配合慢日志、I/O 与事务耗时观察。

复制场景再处理二进制日志写放大
存储层的部分更新和复制日志不是一回事。MySQL 8.4 中,行格式复制默认仍可能把 JSON 完整文档写入 after-image;可以在确认副本版本和运维流程支持后设置:
-- 仅对当前会话启用,先在灰度链路观察副本应用情况 SET SESSION binlog_row_value_options = 'PARTIAL_JSON'; -- 生产环境通常由运维配置持久化,而不是把全局变更写进业务事务 -- SET GLOBAL binlog_row_value_options = 'PARTIAL_JSON';
官方说明中,PARTIAL_JSON 只影响行格式日志中 JSON 修改部分的记录方式;如果服务端无法生成部分更新,仍会使用完整文档。语句格式复制对 JSON 部分更新有自己的记录规则,因此不要只看一个会话变量就推断所有链路都变小。
常见问题
JSON_SET 和 JSON_REPLACE 怎么选?
希望路径不存在时自动新增,用 JSON_SET();只允许替换已有路径,用 JSON_REPLACE(),这样能避免拼写错误悄悄创建新键。
JSON_STORAGE_FREE 返回 0 就代表没有更新成功吗?
不代表。它只表示当前没有可报告的空闲空间;更新可能成功,但新值更大、表达式不满足原地条件,或旧空间被重新利用后没有剩余空洞。
把整个 JSON 文档放进变量再 JSON_SET 还算部分更新吗?
对用户变量而言,值会被完整替换;要让列级部分更新生效,应直接在 UPDATE 中以目标 JSON 列作为 JSON_SET() 的输入。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
334 收藏
-
311 收藏
-
416 收藏
-
365 收藏
-
254 收藏
-
358 收藏
-
228 收藏
-
462 收藏
-
130 收藏
-
数据库 · MySQL | 1小时前 | MySQL · JSON · 数据校验 · JSON_SCHEMA_VALID CHECK 约束 MySQL JSON Schema JSON 结构校验 JSON_SCHEMA_VALIDATION_REPORT293 收藏
-
122 收藏
-
208 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习