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

MySQL JSON_STORAGE_SIZE 怎样估算 JSON 文档占用空间

来源:17golang原创

时间:2026-10-18 10:09:31 330浏览 收藏

排查 JSON 列容量时,最容易混淆的是三个数字:原始字符串的字符长度、字符串按字符集编码后的字节数,以及 MySQL 把 JSON 解析成二进制表示后的存储字节数。标题里的 JSON_STORAGE_SIZE() 解决的是第三个问题。

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

要估算单个 JSON 值在 MySQL JSON 表示中的空间,直接对它调用 JSON_STORAGE_SIZE()。如果参数是 JSON 列,结果应按“插入该值时的 JSON 存储大小”理解;发生部分更新后,再结合 JSON_STORAGE_FREE() 看可复用空间。它不是整张表的磁盘占用统计。

下面用一个很小的 JSON 文档把这个判断拆开。示例只用于说明 SQL 关系和返回字段,不把文中的示例结果当成某台服务器的运行截图。

先区分 JSON 文本长度和二进制存储大小

如果先用 CHAR_LENGTH() 或 LENGTH(),得到的是字符串层面的长度;它们适合检查输入文本,却不能直接代替 JSON 二进制存储大小。MySQL 需要先把合法 JSON 解析成自己的二进制表示,键名、数组、对象和标量会共同影响最终字节数。

因此,容量排查的第一个小项目目标不是计算整张表,而是对同一份 JSON 同时记录“输入文本”和“JSON 存储字节”两个字段:

-- 准备一份可重复比较的 JSON 文档,变量值必须是合法 JSON
SET @doc = '{"user_id":1001,"roles":["editor","reviewer"],"active":true}';

-- 把字符串层面的长度与 JSON 二进制表示的大小放在同一行观察
SELECT
    CHAR_LENGTH(@doc) AS text_chars,
    LENGTH(@doc) AS text_bytes,
    JSON_STORAGE_SIZE(@doc) AS json_bytes;

这里的 json_bytes 才是这份 JSON 值经过解析后、按 JSON 二进制表示计算出的字节数。它和前两个字段不必相等,所以不要用字符串长度乘一个固定比例去推算所有文档。

JSON_STORAGE_SIZE 将 JSON 文档和字符串输入关联到解析后的二进制 JSON 与 bytes 字段的静态结构说明图
图1:JSON_STORAGE_SIZE 的数据关系说明图,展示 JSON 输入、二进制表示和 bytes 字段之间的静态关系;这是说明图,不是运行截图。

用 JSON_STORAGE_SIZE 读取文档占用字节

把函数放到查询列里即可批量比较候选文档。参数可以是 JSON 列,也可以是能解析成 JSON 的字符串。对字符串参数,MySQL 会先解析再计算;参数为 NULL 时返回 NULL,参数不是合法 JSON 时则报错,这两个边界要和“返回 0”区分开。

-- 建立最小示例表,保留 JSON 类型以观察列级语义
CREATE TABLE json_size_demo (
    id INT PRIMARY KEY,
    payload JSON NOT NULL
);

-- 插入两个结构不同但都合法的 JSON 文档
INSERT INTO json_size_demo (id, payload) VALUES
    (1, '{"name":"A","items":[1,2,3]}'),
    (2, '{"name":"A","items":[1,2,3],"note":"longer value"}');

-- 返回每条 JSON 列值的存储字节数,便于按文档比较
SELECT
    id,
    JSON_STORAGE_SIZE(payload) AS json_bytes
FROM json_size_demo
ORDER BY id;

结果列只回答“这一列中的 JSON 文档在该函数语义下用了多少字节”。如果要做容量趋势,可以按业务维度分组后取样或汇总,但仍要注明这不是数据页、索引和压缩文件的完整大小。

JSON 列发生部分更新后,为什么结果要谨慎解释

JSON 列支持部分更新时,MySQL 可能在原有二进制空间中复用空闲区域。官方文档对列参数有一个很关键的限定:JSON_STORAGE_SIZE(json_col) 表示该 JSON 值插入列时使用的空间,而部分更新后留下的可用空间要看 JSON_STORAGE_FREE(json_col)。

-- 先对 JSON 列做一次局部替换,模拟文档内部字段变化
UPDATE json_size_demo
SET payload = JSON_SET(payload, '$.note', 'short')
WHERE id = 1;

-- 同时观察插入时大小语义与部分更新后的可复用空间
SELECT
    id,
    JSON_STORAGE_SIZE(payload) AS inserted_json_bytes,
    JSON_STORAGE_FREE(payload) AS reusable_bytes
FROM json_size_demo
WHERE id = 1;

不要把 inserted_json_bytes 直接当成当前行在磁盘页里的精确占用,也不要把 reusable_bytes 当成已经归还给文件系统的空间。它们分别帮助你理解 JSON 值的初始二进制大小和部分更新留下的内部可复用空间。

JSON 列、插入时大小、部分更新、JSON_STORAGE_SIZE 与 JSON_STORAGE_FREE 的静态关系说明图
图2:JSON 列空间字段关系说明图,展示插入时大小、部分更新后的可复用空间与表级边界;这是说明图,不是运行截图。

把结果接进容量排查,而不是表空间估算

实际排查可以按下面的顺序落地:

  1. 先对采样 JSON 计算 JSON_STORAGE_SIZE(),确认不同结构和字符串长度的差异。
  2. 对 JSON 列同时观察 JSON_STORAGE_FREE(),判断是否存在部分更新留下的可复用空间。
  3. 如果问题是表目录增长,再单独查看表数据、索引、页分配、引擎和备份文件;不要把单列函数结果冒充整表统计。

这样做的好处是把“文档本身变大了吗”和“表在磁盘上为什么变大了”拆成两个可复查的问题。前者用 JSON 函数回答,后者需要数据库存储层指标配合。

常见疑问

JSON_STORAGE_SIZE(NULL) 会返回多少? 返回 NULL,不要把它当作零字节。若参数既不是 NULL 又不能解析成 JSON,则会报错。

它能直接告诉我表占了多少磁盘吗? 不能。它面向一个 JSON 值或 JSON 列值,表空间还可能包含行记录、索引、页内碎片、压缩和其他列。

为什么同样字符数的两个 JSON 结果不同? 键名、层级、数组/对象结构和标量类型都可能改变二进制表示;应以同一 MySQL 环境下的函数结果做比较。

总结一下:用 JSON_STORAGE_SIZE() 取得 JSON 值的二进制存储字节数,用 JSON_STORAGE_FREE() 补充部分更新后的可复用空间,再把表级磁盘问题交给存储层指标。这样既能定位 JSON 文档是否异常膨胀,也不会把一个单值函数误用成整表容量审计。

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