当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL JSON partial update 为什么 JSON_SET 没有触发原地更新

MySQL JSON partial update 为什么 JSON_SET 没有触发原地更新

来源:17golang原创 2026-10-24 18:10:11 0浏览 收藏

如果你写了 UPDATE profile SET doc = JSON_SET(doc, '$.name', 'Li'),却发现 JSON 文档仍像被整体重写,先不要把原因归结为 JSON_SET 失效。MySQL 的 JSON partial update 是优化器在满足一组条件时才会选择的路径,函数名本身只是候选入口,并不是原地更新的保证。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/

最常见的判断顺序是:列必须是 JSON 类型;输入列和被更新的目标列必须是同一列;更新要替换已有对象或数组值,不能给父对象或数组添加新元素;新值通常不能比原值占用更多空间。任一条件不满足,都可能退回完整 JSON 更新。

JSON_SET 只是进入候选路径的入口

MySQL 8.4 文档列出的部分更新函数包括 JSON_SET()、JSON_REPLACE() 和 JSON_REMOVE()。优化器会分析 UPDATE 的表达式,只有能够把变更表示成 JSON 二进制差分,并交给支持该能力的存储引擎时,才可能减少完整文档的重写。

MySQL JSON_SET 部分更新候选条件与 InnoDB 存储协作的静态结构图
图1:结构说明图,展示 UPDATE、JSON_SET、已有路径、替换值和 InnoDB 之间的静态关系;这不是数据库控制台截图。
-- 只有 doc 列本身是 JSON,且表达式读写同一列,才具备候选形态。
UPDATE profile
SET doc = JSON_SET(doc, '$.name', 'Li')
WHERE id = 7;

-- 直接赋值会生成新的 JSON 值,不属于 JSON_SET 的部分更新形态。
UPDATE profile
SET doc = '{"name":"Li","level":3}'
WHERE id = 7;

因此,“我已经用了 JSON_SET”只能证明语句走到了候选入口,不能证明某一行一定发生了原地写入。尤其是多行更新时,每一行的 JSON 路径是否存在、旧值空间是否够用,都可能不同。

四个条件逐项排查为什么没有原地更新

第一,目标字段要在表结构中声明为 JSON,而不是把 JSON 文本存进 TEXT。第二,函数的输入列和赋值目标必须相同;SET doc_a = JSON_SET(doc_b, ...) 不符合部分更新的同列条件。第三,路径需要指向已有值,替换父对象或数组中的既有值可以被考虑,但给父对象新增成员或把数组扩展到末尾会改变结构。

第四,新值要能放回原值所在的空间。把较长字符串写入较短字符串时,通常没有足够的空洞;如果之前的部分更新留下了可复用空间,较大的值也可能被容纳,所以不能只凭字符串长度做绝对判断。

-- 这是“替换已有路径”的候选写法;实际是否原地仍取决于空间。
UPDATE profile
SET doc = JSON_SET(doc, '$.name', 'Li')
WHERE id = 7;

-- 这是新增路径,改变了父对象结构,不能按同一条件理解为原地替换。
UPDATE profile
SET doc = JSON_SET(doc, '$.nickname', '小李')
WHERE id = 7;

-- 输入列与目标列不同,优化器不能把它当成同列部分更新。
UPDATE profile
SET doc_a = JSON_SET(doc_b, '$.name', 'Li')
WHERE id = 7;

排查时要把“路径不存在”“新值变长”“表达式读写不同列”和“字段实际不是 JSON”分开记录。它们表面上都表现为 JSON_SET 执行成功,但对存储层的更新机会完全不同。

用 JSON_STORAGE_FREE 观察可复用空间

JSON_STORAGE_FREE() 反映 JSON 列二进制表示中因部分更新释放、可继续利用的空间;JSON_STORAGE_SIZE() 则反映当前文档占用的存储大小。一个较短的替换值成功留下可用空间时,前者可能增加;直接给 JSON 列赋完整新文档时,前者会回到零。

MySQL JSON_STORAGE_FREE、已有路径、新值大小与完整赋值边界的静态关系图
图2:边界结构图,说明已有路径、替换值空间、JSON_STORAGE_FREE 与直接赋值之间的关系;这不是运行结果截图。
-- 先查看当前文档大小与可复用空间。
SELECT JSON_STORAGE_SIZE(doc) AS used_bytes,
       JSON_STORAGE_FREE(doc) AS free_bytes
FROM profile
WHERE id = 7;

-- 用更短的值替换已有值,可能留下可复用空间。
UPDATE profile
SET doc = JSON_SET(doc, '$.description', '短说明')
WHERE id = 7;

-- 这里的两个函数用于观察存储状态,不是强制打开部分更新的开关。
SELECT JSON_STORAGE_SIZE(doc), JSON_STORAGE_FREE(doc)
FROM profile
WHERE id = 7;

这个观察值适合帮助定位“为什么这一行没有复用原空间”,但不能把它当成通用性能证明。实际收益还取决于 JSON 文档大小、变更比例、存储引擎和整体写入负载。

别把存储层更新和 binlog 部分记录混为一谈

MySQL 8.4 还支持在 row-based replication 下用 binlog_row_value_options=PARTIAL_JSON 记录 JSON 变更的部分内容。这是二进制日志的表示方式,和表中 JSON 值是否在存储层原地修改不是同一个开关。即使日志可以采用部分格式,也不能据此反推每一行都完成了存储层原地更新。

-- 只在确认需要压缩 row-based JSON 变更日志时设置。
SET SESSION binlog_row_value_options = 'PARTIAL_JSON';

-- 如果服务器无法生成 JSON 部分差分,日志仍可能记录完整文档。
UPDATE profile
SET doc = JSON_SET(doc, '$.name', 'Li')
WHERE id = 7;

可以把最终排查清单压缩为六个问题:字段是不是 JSON?表达式是不是同列读写?路径原来是否存在?是否新增了父对象或数组元素?新值是否有机会放进原空间?你观察的到底是列存储、InnoDB 写入,还是 binlog 体积?逐项回答后,通常就能解释为什么 JSON_SET 执行成功,却没有出现预期的“原地更新”。

相关问题

JSON_SET 执行成功就代表发生了部分更新吗?

不代表。它只说明 JSON 表达式得到结果;是否采用部分更新还要看列类型、同列条件、路径是否为已有值以及替换空间等条件。

JSON_STORAGE_FREE 大于零是不是性能变快的证明?

不是。它说明二进制 JSON 表示中有可复用空间,不能单独代表磁盘 I/O、锁等待或复制延迟已经改善。

参考资料:https://dev.mysql.com/doc/refman/8.4/en/json.html、https://dev.mysql.com/doc/refman/8.4/en/json-utility-functions.html、https://dev.mysql.com/doc/refman/8.4/en/replication-features-json.html

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL named window 怎样复用多个查询的窗口定义MySQL named window 怎样复用多个查询的窗口定义
上一篇
MySQL named window 怎样复用多个查询的窗口定义
MySQL multi-valued index 怎样为 JSON 数组建立多值索引
下一篇
MySQL multi-valued index 怎样为 JSON 数组建立多值索引
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    661次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    681次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    727次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    693次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    527次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码