Redis 中 EMBSTR 对象的阈值设置为 44 字节 是经过深思熟虑的设计决策,主要目的是优化内存使用和性能。以下是详细解析:
一、44 字节阈值的由来
1. 内存分配与缓存行优化
Redis 默认使用 jemalloc 内存分配器,其分配内存的最小单位是 2^n 字节。为了减少内存碎片,Redis 尽量将对象大小对齐到 64 字节(现代 CPU 缓存行的常见大小)。
- 计算公式:
可用空间 = 64 字节(缓存行) - Redis 对象头(robj) - SDS 头(sdshdr8) - 结束符(NULL)
- Redis 对象头(robj):16 字节(64 位系统)。
- SDS 头(sdshdr8):3 字节(len: 1 字节,alloc: 1 字节,flags: 1 字节)。
- 结束符(NULL):1 字节。
- 最终可用空间:64 – 16 – 3 – 1 = 44 字节。
2. 避免内存碎片
jemalloc 分配内存时,若请求的大小小于等于 64 字节,会直接分配一个 64 字节的块(无碎片)。EMBSTR 通过将字符串内容嵌入到连续的内存块中,避免了多次内存分配(如 RAW 编码的两次分配),从而减少内存碎片。
3. 实际工作负载适配
统计表明,大多数短字符串(如键名、缓存标签、计数器等)长度在 44 字节以内。设置此阈值能覆盖常见场景,同时兼顾性能和内存效率。
二、EMBSTR 与 RAW 编码的区别
维度 | EMBSTR(≤44 字节) | RAW(>44 字节) |
---|---|---|
内存分配次数 | 一次分配(robj + SDS 头 + 字符串内容) | 两次分配(robj 和 SDS 头 + 字符串内容) |
内存局部性 | 连续内存块,减少缓存未命中 | 非连续内存块,可能增加缓存未命中 |
内存碎片 | 更少(无额外碎片) | 可能产生碎片(两次分配) |
性能 | 更快(减少内存操作次数) | 略慢(需两次分配和释放) |
三、EMBSTR 阈值的调整历史
Redis 的 EMBSTR 阈值并非一成不变,而是随着版本迭代逐步优化的:
版本 | 阈值(字节) | 调整原因 |
---|---|---|
Redis 2.x | 无 EMBSTR | 所有字符串使用 RAW 编码。 |
Redis 3.0 | 39 字节 | 初次引入 EMBSTR,基于当时的 robj 和 sdshdr 结构计算(未优化 SDS 头)。 |
Redis 3.2 | 44 字节 | 优化 SDS 结构(改为 sdshdr8 ),减少 SDS 头占用(从 8 字节 → 3 字节)。 |
Redis 4.0+ | 44 字节 | 保持稳定,经验证此阈值在性能和内存效率之间达到最佳平衡。 |
关键调整点
- SDS 头优化:
Redis 3.2 引入了sdshdr8
、sdshdr16
等更小的 SDS 头结构,针对短字符串减少内存浪费。例如:- 原
sdshdr
占 8 字节(unsigned int len
+unsigned int free
)。 - 优化后
sdshdr8
占 3 字节(uint8_t len
+uint8_t alloc
+char flags
)。 - 节省 5 字节,使得 EMBSTR 最大容量从 39 字节提升到 44 字节。
- 原
四、阈值调整的注意事项
- 源码修改:
若需调整阈值,需修改 Redis 源码中的常量OBJ_ENCODING_EMBSTR_SIZE_LIMIT
,并重新编译。#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
- 性能测试:
调整后需进行充分测试,验证以下指标:- 内存使用率是否优化。
- 高并发场景下的性能(如 QPS、延迟)。
- 是否出现内存碎片或缓存未命中增加。
- 适用场景:
- 建议保留默认值:44 字节已适配大多数短字符串场景。
- 特殊场景调整:如存储大量中等长度字符串(如日志、JSON 片段),可适当调高阈值,但需权衡内存与性能。
五、总结
Redis 将 EMBSTR 阈值设为 44 字节的核心目标是:
- 充分利用 64 字节缓存行,减少内存碎片。
- 平衡内存使用与访问效率,通过连续内存块提升性能。
- 适配实际工作负载,覆盖大多数短字符串场景。
这一设计经过多个版本的优化验证,是 Redis 内存优化的经典案例。开发者在使用 Redis 时,应尽量让字符串长度小于 44 字节,以充分利用 EMBSTR 编码的优势。
THE END