Redis 中 EMBSTR 对象的阈值设置为何为 44?其调整历史是什么?
Redis 中 EMBSTR 对象的阈值设置为何为 44?其调整历史是什么?
为什么 EMBSTR 的阈值大小是 44 个字节?
这个问题有几个关键点:
1)Redis 使用的是 jemalloc 作为内存分配器。
2)jemalloc 是以 64 字节作为内存单位进行内存分配的,如果超过了 64 字节,即超过了一个内存单元,使用的就是 raw 编码,反之使用的就是 EMBSTR 编码。
3)核心就是这个 64 字节,围绕 64 字节这个关键点来分析。Redis 的字符串对象是 redisObject 和 sdshdr 这两个部分组成的,redisObject 大小为
4 + 4 + 24 + 32 + 64 = 128bits = 16 bytes(16 字节)
这个是一直没有改变的,其计算的来源如下所示:
1 | // from Redis 3.9.5 |
然后我们再来看一下 sdshdr 的结构:
1 | // from Redis 3.9.5 |
sdshdr 占用的内存大小:3 byte + 字符数组的大小,由于字符数组内部保留的一个’\0’的占位符,所以剩下能用的空间就只有 44 个字节了。
那为什么之前版本的阈值是 39 呢?
其主要还是因为 sds 结构的版本差异,在 3.2 以前 sdshdr 的版本结构如下:
1 | struct SDS { |
我们可以看到,非数据字段就占用了 8 个字节,为了节约内存,3.2 版本之后的sds不再使用 sdshdr5 这个结构了,就剩下 sdshdr8、sdshdr16、sdshdr32、sdshdr64 这 4 个结构。
然后 EMBSTR 使用 sdsjdr8 节约了 6 个字节,然后多引入一个 flags 字段占用 1 字节,所以现版本的 EMBSTR 相比 3.2 版本之前的 sds 多了 5 个字节。
总结
主要是因为 Redis 使用 jemalloc 内存分配器,jemalloc 以 64 字节作为阈值区分大小字符串 raw 和 EMBSTR。
然后 redisObject 固定占用 16 个字节,然后 sdshdr 中已分配、已申请、标记这 3 个字段各自占用 1 个字节,’\0’占用 1 个字节,最终剩余 44 个字节。
因为 3.2 前后 Redis 关于 sdshdr 结构的差异,3.2 之后的版本 EMBSTR 使用 sdshdr8 这个结构,总容量和已使用容量字段减少了 6 个字节,但是 3.2 之后的版本增加了一个 flags 字段,所以最终 3.2 版本之前的 EMBSTR 结构少了 5 个字节。