登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL JSON_STORAGE_FREE 怎样判断部分更新留下的空间

来源:17golang原创

时间:2026-10-23 03:51:25 347浏览 收藏

如果你想判断 MySQL 的 JSON 列在更新后是否留下了可以继续利用的空间,关键不是直接看 JSON 文本有多长,而是查询 JSON_STORAGE_FREE(json_col)。这个函数返回的是:JSON 列的二进制表示在合适的部分更新后释放、保留下来的空间。

官方地址:https://dev.mysql.com/doc/refman/8.4/en/json-utility-functions.html

我排查这类问题时,通常先确认三件事:参数确实来自表中的 JSON 列;更新表达式使用了 JSON_SET()、JSON_REPLACE() 或 JSON_REMOVE();本次修改是替换已有值,而不是强行写入更大的新文档。下面按这个顺序拆开。

先区分 JSON 列与普通 JSON 值

JSON_STORAGE_FREE() 接受 JSON 列、合法 JSON 字符串、字面量和用户变量,但它们的语义不同。只有表中的 JSON 列经历了可利用的原地部分更新,才可能得到正数。对字面量或用户变量调用,即使表达式里写了 JSON_SET(),也不会产生表列那种“更新后留下的空闲空间”。

因此,下面的查询适合作为排查入口:

-- 读取列当前文档、初始二进制尺寸和部分更新留下的空间
SELECT
    id,
    JSON_STORAGE_SIZE(payload) AS storage_size,
    JSON_STORAGE_FREE(payload) AS storage_free
FROM event_log
WHERE id = 1001;

JSON_STORAGE_SIZE() 用来观察 JSON 二进制表示的尺寸,JSON_STORAGE_FREE() 用来观察部分更新累计留下的空闲空间。两者回答的是不同问题,不能把其中一个当成另一个的替代品。

用 JSON_SET 做一次可观察的部分更新

要看到正数,最容易复现的方式是把已有字符串替换成更短的值,并且让输入列和目标列保持为同一列:

-- 只替换已有路径,给优化器留下执行部分更新的机会
UPDATE event_log
SET payload = JSON_SET(payload, '$.message', 'ok')
WHERE id = 1001;

-- 读取更新后保留下来的空闲空间
SELECT
    JSON_STORAGE_SIZE(payload) AS storage_size,
    JSON_STORAGE_FREE(payload) AS storage_free
FROM event_log
WHERE id = 1001;

假设原来的 $.message 值比 ok 长,且其他条件满足,storage_free 可能返回正数。这个数表示释放出来的空间,不代表数据库已经把它返还给操作系统,也不代表整行都没有重写。

JSON 列通过 JSON_SET 进行部分更新后留下可复用空间的结构示意图
图1:JSON 列部分更新的调用关系示意图,展示已有值变短后空闲空间如何被记录;这是静态说明图,不是截图或运行证据。

用 JSON_STORAGE_SIZE 对照结果

一个容易误解的地方是:部分更新后,JSON_STORAGE_SIZE(payload) 仍然可能反映该列最初写入时的二进制存储尺寸,而 JSON_STORAGE_FREE(payload) 单独记录后续部分更新释放的空间。因此,看到尺寸没有下降,并不等于更新没有生效。

-- 用一次更短的已有值替换,观察尺寸与空闲空间的组合
UPDATE event_log
SET payload = JSON_SET(payload, '$.level', 'i')
WHERE id = 1001;

-- 尺寸用于看基线,空闲空间用于看部分更新的累计结果
SELECT
    JSON_STORAGE_SIZE(payload) AS original_binary_size,
    JSON_STORAGE_FREE(payload) AS free_after_partial_updates
FROM event_log
WHERE id = 1001;

连续多次合适的部分更新,空闲空间可以累计增加。反过来,如果后一次修改重新占用了这部分空间,返回值也应按当前列的二进制状态理解,而不是当作一条独立更新的日志。

排查为什么结果总是 0

返回 0 很常见,通常先看更新写法而不是怀疑函数:

场景典型写法或条件结果判断
直接给列赋 JSON 文本SET payload = '{"a":1}'完整替换,通常为 0
使用用户变量JSON_STORAGE_FREE(@payload)不是表列部分更新,返回 0
更新表达式输入输出不是同一列SET payload_a = JSON_SET(payload_b,...)不满足列内原地更新条件
新增成员或新值更大替换空间不足,或路径原本不存在可能退回完整更新,返回 0
参数为 NULLJSON_STORAGE_FREE(NULL)返回 NULL

这里的“可能”很重要:是否采用部分更新由优化器根据列类型、表达式形式、路径和值大小等条件决定,不能只看到 JSON_SET() 就断言一定会得到正数。

JSON_STORAGE_FREE 返回正数、零或 NULL 的条件对照结构图
图2:JSON_STORAGE_FREE 结果判断矩阵,区分部分更新、完整替换、字面量和 NULL;这是静态结果示意图,不是截图或运行证据。

形成一套可复用的排查清单

  1. 先确认列定义确实是 JSON,不要把普通文本列的 JSON 字符串当成同一种情况。
  2. 确认 UPDATE 的输入列和目标列相同,并优先使用 JSON_SET、JSON_REPLACE 或 JSON_REMOVE。
  3. 确认路径原来存在,替换值没有变得更大;如果是新增成员,先按完整更新来预期。
  4. 同时读取 JSON_STORAGE_SIZE 和 JSON_STORAGE_FREE,不要只看 JSON 文本长度。
  5. 把正数理解为列内部可复用的空闲空间,不把它当作磁盘回收量或整行写入量。

常见问题

JSON_STORAGE_FREE 返回 0 是函数失效吗?

通常不是。直接赋值、使用字面量或用户变量、值变大、增加新成员,以及不满足原地更新条件时,返回 0 都是正常表现。

JSON_STORAGE_FREE 返回的数字就是当前 JSON 大小吗?

不是。它表示部分更新后保留下来的空闲空间;当前二进制尺寸和基线应结合 JSON_STORAGE_SIZE 理解。

只要用 JSON_SET 就一定是部分更新吗?

不一定。还要满足列类型、输入输出列一致、替换已有值且空间条件合适等约束,最终由 MySQL 判断是否能采用部分更新。

小结:判断部分更新留下的空间,先锁定表中的 JSON 列,再看更新函数和替换值,最后用 JSON_STORAGE_SIZE 与 JSON_STORAGE_FREE 组合解释结果。这样即使返回 0,也能快速定位是写法、值大小还是参数类型导致的。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>