面试题:Redis 中 EMBSTR 对象的阈值设置为何为 44?其调整历史是什么?

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.039 字节初次引入 EMBSTR,基于当时的 robj 和 sdshdr 结构计算(未优化 SDS 头)。
Redis 3.244 字节优化 SDS 结构(改为 sdshdr8),减少 SDS 头占用(从 8 字节 → 3 字节)。
Redis 4.0+44 字节保持稳定,经验证此阈值在性能和内存效率之间达到最佳平衡。

关键调整点

  • SDS 头优化
    Redis 3.2 引入了 sdshdr8sdshdr16 等更小的 SDS 头结构,针对短字符串减少内存浪费。例如:
    • 原 sdshdr 占 8 字节(unsigned int len + unsigned int free)。
    • 优化后 sdshdr8 占 3 字节(uint8_t len + uint8_t alloc + char flags)。
    • 节省 5 字节,使得 EMBSTR 最大容量从 39 字节提升到 44 字节。

四、阈值调整的注意事项

  1. 源码修改
    若需调整阈值,需修改 Redis 源码中的常量 OBJ_ENCODING_EMBSTR_SIZE_LIMIT,并重新编译。#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
  2. 性能测试
    调整后需进行充分测试,验证以下指标:
    • 内存使用率是否优化。
    • 高并发场景下的性能(如 QPS、延迟)。
    • 是否出现内存碎片或缓存未命中增加。
  3. 适用场景
    • 建议保留默认值:44 字节已适配大多数短字符串场景。
    • 特殊场景调整:如存储大量中等长度字符串(如日志、JSON 片段),可适当调高阈值,但需权衡内存与性能。

五、总结

Redis 将 EMBSTR 阈值设为 44 字节的核心目标是:

  1. 充分利用 64 字节缓存行,减少内存碎片。
  2. 平衡内存使用与访问效率,通过连续内存块提升性能。
  3. 适配实际工作负载,覆盖大多数短字符串场景。

这一设计经过多个版本的优化验证,是 Redis 内存优化的经典案例。开发者在使用 Redis 时,应尽量让字符串长度小于 44 字节,以充分利用 EMBSTR 编码的优势。

THE END
喜欢就支持一下吧
点赞9 分享