JSON 内存占用
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/data-types/json/ram/
由于功能不断添加、改进和优化,JSON 的内存消耗可能因 Redis 版本而异。本页示例使用的是 Redis Open Source 中的 Redis 8。
Redis 中的每个键都会占用内存,至少需要存储键名所需的 RAM,以及 Redis 使用的每个键的一些开销。除此之外,键中的值也需要 RAM。
Redis JSON 将 JSON 值以反序列化后的二进制数据形式存储。这种表示方式在大小上通常比序列化形式更耗费空间。所有 JSON 值至少占用 8 个字节(在 64 位架构上),因为每个值都被表示为指向某个对象的薄封装。类型信息存储在指针的低位中,由于对齐限制,这些位被保证为零。这使得这些位可以被重新利用来存储一些辅助数据。
对于某些类型的 JSON 值,8 个字节就足够了。Null 和布尔值不需要任何额外的存储。小整数存储在静态内存中,因为它们经常被使用,因此也仅使用初始的 8 个字节。类似地,空字符串、空数组和空对象也不需要任何簿记信息。相反,它们指向一个 空 字符串、数组或对象的静态实例。以下是一些使用 JSON.DEBUG MEMORY 命令报告内存消耗的示例:
127.0.0.1:6379> JSON.SET boolean . 'true'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY boolean
(integer) 8
127.0.0.1:6379> JSON.SET null . null
OK
127.0.0.1:6379> JSON.DEBUG MEMORY null
(integer) 8
127.0.0.1:6379> JSON.SET emptystring . '""'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptystring
(integer) 8
127.0.0.1:6379> JSON.SET emptyarr . '[]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptyarr
(integer) 8
127.0.0.1:6379> JSON.SET emptyobj . '{}'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY emptyobj
(integer) 8这个内存需求对于所有标量值都是相同的,但字符串根据其长度需要额外的空间。例如,一个 3 个字符的字符串将使用额外的 3 个字节:
127.0.0.1:6379> JSON.SET foo . '"bar"'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY foo
(integer) 11在以下四个示例中,每个数组需要 56 个字节。其构成如下:
- 初始数组值指针 8 字节
- 元数据 16 字节:8 字节用于已分配容量,8 字节用于数组的即时大小
- 数组本身 32 字节。数组的初始容量为 4。因此,计算方式为
4 * 8字节
127.0.0.1:6379> JSON.SET arr . '[""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56127.0.0.1:6379> JSON.SET arr . '["", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56127.0.0.1:6379> JSON.SET arr . '["", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56127.0.0.1:6379> JSON.SET arr . '["", "", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 56一旦当前容量不足以容纳新值,数组将重新分配为当前容量的两倍。一个有 5 个元素的数组将具有 8 的容量,因此消耗 8 + 16 + 8 * 8 = 88 字节。
127.0.0.1:6379> JSON.SET arr . '["", "", "", "", ""]'
OK
127.0.0.1:6379> JSON.DEBUG MEMORY arr
(integer) 88由于重新分配操作可能代价较高,Redis 以几何级数而非线性方式增长 JSON 数组。这种方法将成本分摊到多次插入中。
下表给出了来自模块仓库的一些测试文件在使用 JSON 存储时的大小(字节)。MessagePack 列仅供参考,反映了使用 MessagePack 存储时值的长度。
| 文件 | 文件大小 | Redis JSON | MessagePack |
|---|---|---|---|
| /tests/files/pass-100.json | 381 | 1069 | 140 |
| /tests/files/pass-jsonsl-1.json | 1387 | 2190 | 757 |
| /tests/files/pass-json-parser-0000.json | 3718 | 5469 | 2393 |
| /tests/files/pass-jsonsl-yahoo2.json | 22466 | 26901 | 16869 |
| /tests/files/pass-jsonsl-yelp.json | 46333 | 57513 | 35529 |
注意:在当前版本中,从容器中删除值 不会 释放容器的已分配内存。
JSON 字符串复用机制
Redis 使用全局字符串复用机制来减少内存使用。当一个字符串值多次出现时,无论是在同一个 JSON 文档内,还是在同一节点上的不同文档之间,Redis 只存储该字符串的一个副本,并使用引用来指向它。这种方法在多个文档共享相似结构时尤其高效。
然而,JSON.DEBUG MEMORY 命令报告的内存使用情况是假定每个字符串实例都是独立存储的,即使它实际上被复用了。例如,文档 {"foo": ["foo", "foo"]} 在内部复用了字符串 "foo",但报告的内存使用量会将该字符串计数三次:一次作为键,一次作为每个数组元素。