Redis String编码原理与压测实战:int、embstr、raw性能差异全解析 1. 内容整体设计与思路拆解1.1 为什么要死磕 String 的编码问题很多人学 Redis 的时候都背过这么一句口诀String 类型有三种编码int、embstr 和 raw阈值是 44 字节。背完之后呢大多数人在实际开发里根本不会去查一个 key 到底用的是哪种编码更不会去想这个 44 字节到底是怎么算出来的以及它对线上性能到底有多大影响。我之所以花时间做这一轮压测是因为前阵子review一个老项目的缓存层发现有人把一堆 100 字节左右的小 JSON 字符串全塞进 Redis线上出现了一些偶发的延迟毛刺。有人怀疑是网络问题有人怀疑是 Redis 本身的问题。我当时就反问了一句你们知不知道这个字符串在 Redis 内部是怎么存的有没有看过 object encoding 这个命令的输出沉默。然后我去看了一下发现这些 key 的存储编码全是 raw。虽然说不上这就是延迟毛刺的根因但从那一刻起我就意识到很多开发者对 Redis String 的编码机制确实停留在背结论的阶段。44 字节这个数字他们知道但 44 字节为什么是 44编码不同对性能的影响到底有多大几乎没人真正用压测数据验证过。这篇博文就是把这件事彻底讲透int、embstr、raw 三种编码各自的内存布局、转换条件、真实性能差距以及 12 轮压测下来的实测数据。为了确保结论不是拍脑袋我会展示完整的压测设计思路、关键参数和对比结果最后再给出一套可以直接落地的编码选型建议。1.2 从源码层面理解三种编码的本质区别在贴压测数据之前有件事必须先讲清楚否则后面所有数字都像是空中楼阁——这三种编码本质上不是 Redis 开发者拍脑袋设计的三种格式而是三个不同生命周期、不同分配策略的存储方案。int 编码最好理解。当一个字符串能完整解析成一个 64 位有符号整数时Redis 直接把整数值存在 redisObject 的 ptr 字段里根本不需要额外的指针、不需要 malloc、不需要额外的内存分配。这种情况下一个 key 的存储成本极低而且因为数字可以直接参与运算像 INCR、DECR 这类命令无需先做字符串到数字的转换天然就快。embstr 编码针对的是短字符串确切地说是小于等于 44 字节的字符串。它的核心思路是一次性把 redisObject 结构体和 SDSSimple Dynamic String简单动态字符串结构体分配在同一个连续内存块里。这是什么概念就是说Redis 申请一次内存就把对象头和字符串内容都搞定了整个过程只有一次 malloc释放的时候也只要一次 free。这样既减少了内存分配的系统调用次数又充分利用了 CPU 缓存因为对象头和字符串数据在内存里是挨着的访问起来非常快。raw 编码则是兜底方案。当字符串长度超过 44 字节或者经过某些修改操作导致长度变化Redis 就只能使用两次独立的内存分配一次给 redisObject一次给 SDS。这就意味着多了一次 malloc 的开销而且在 jemalloc 这样的分配器下两个内存块大概率不会相邻缓存局部性就差了。我打个比方。embstr 就像你去便利店一个塑料袋里既装了面包又装了牛奶拎着就走raw 是你在超市推着购物车面包放在烘焙区货架上牛奶放在冷藏区货架上你得分别扫码、分别装袋。最终你拿到的都是面包加牛奶但过程的开销完全不同。好那 44 字节这个阈值是怎么来的这里必须展开说因为这是很多人的知识盲区。1.3 44 字节这个数字到底从哪里来先说结论这个阈值不是随便定的更不是性能测试测出来的经验值而是由内存分配策略决定的数学结果。在 Redis 3.2 之前这个数字是 393.2 之后才变成 44原因就是 SDS 结构体做了优化。要算清楚这个数得先知道几个前提。第一Redis 默认使用 jemalloc 内存分配器jemalloc 有一个非常出名的能力把内存分配请求按大小分为不同的 size class其中最常用的一个排列是 8、16、32、64 等也就是说如果你申请的内存大小在 33 到 64 字节之间jemalloc 实际上会给你分配一个 64 字节的内存块。第二在 64 位系统上redisObject 结构体的大小是 16 字节。第三Redis 3.2 之后引入了 sdshdr8 结构体它的头部大小是 3 字节存储长度为 1 的字符串时还需要额外 1 字节存结束符虽然 SDS 并不依赖结束符来定位字符串末尾但为了兼容 C 字符串函数依然会保留一个 \0。那么64 字节减掉 redisObject 的 16 字节再减掉 sdshdr8 的 3 字节头再减掉 1 字节的结束符得到64 - 16 - 3 - 1 44这就是 44 字节的精确来源。在这个大小以内一个 redisObject 加一个 SDS 可以正好塞进 jemalloc 的 64 字节 size class 里只用一次 malloc内存零浪费。一旦超过 44 字节就要走 raw 编码redisObject 和 SDS 分开分配内存使用率和访问效率都会有变化。但是在实际压测之前我心里有一个疑问这个阈值的意义更多是内存布局上的分界还是真的会显著影响命令执行的耗时44 字节附近的字符串embstr 和 raw 的 SET/GET 到底能差多少如果差距微乎其微那线上完全不需要因为字符串多了一两个字节而焦虑如果差距明显那设计缓存 key 的时候就得把字符串长度纳入考量了。带着这个疑问我开始设计这轮压测。2. 为什么阈值不是一切压测方案的设计与选型考量2.1 压测目标不测平均延迟测 P99 和真实吞吐很多人在做 Redis 压测的时候就喜欢盯着平均 QPS看测完之后发个朋友圈我压测 Redis 能到 12 万 QPS。这个数字不能说没意义但在定位真实问题时平均值的参考价值非常有限因为 Redis 这种单线程模型下CPU 处理命令的时间本身极短真正影响用户体验的是尾部延迟——也就是 P99、P999。你可以把平均延迟理解为班级平均分全班平均 90 分听起来不错但只要有一个学生考了 30 分总有人是被平均的。在线上如果一个请求偶尔要等 200 毫秒而其他请求只需 0.5 毫秒用户感知到的就是这个系统偶尔很卡。所以说这次压测我重点关注两个指标一是 QPS二是 P99 延迟二者结合起来才能看到编码对命令执行的真实影响。另一个需要考虑的因素是压测命令的选择。字符串操作最核心的无非就几个SET、GET、INCR、APPEND。INCR 主要体现 int 编码的优势APPEND 主要体现 raw 编码在修改场景下的行为SET 和 GET 则是最通用的读写路径。我只测 SET 和 GET 吗不行那样得不到完整的结论。尤其是 APPEND 这个命令它对 embstr 编码有一个非常特殊的影响embstr 是不可修改的一旦对它执行 APPENDRedis 会先把 embstr 转成 raw再执行追加操作。这个过程有额外的转换开销不压一下不知道。基于以上考虑我把压测分成 12 轮从不同字符串长度、不同命令、不同数据分布三个维度来交叉验证确保结论不是偶然的。2.2 测试环境与工具redis-benchmark 之外的准备工作压测工具我选的是官方自带的 redis-benchmark。有些朋友可能习惯用 JMeter 或者自己写多线程客户端这两种方式不是不行但作为对比测试redis-benchmark 有一个不可替代的优势它直接走 Redis 的文本协议最大程度地减少了客户端逻辑对测试结果的干扰。JMeter 适合模拟更复杂业务流程的压测而我的目标是考察 Redis 本身的编码性能差异越裸越好。测试环境我这里也交代一下方便大家复现对比服务器Linux 4.15 内核8 核 CPU16 GB 内存部署在同一台物理机上Redis6.2.6 版本默认配置未开启持久化压测客户端redis-benchmark与 Redis 同机部署避免网络成为瓶颈并发连接数50 个客户端连接单轮压测时间30 秒取稳定后的数据每条命令刷新的输出结果按 10 秒一次的间隔记录每次压测前执行 FLUSHALL避免之前的数据残留影响测试这里特别说明一下Redis 6.x 之后 redis-benchmark 的参数有所变化如果你用的是更早的版本注意 -t 参数和 -d 参数的兼容性。-d 表示数据大小单位是字节这个在测字符串长度对编码的影响时非常关键。2.3 十二轮压测的设计逻辑压测轮次怎么安排其实很有讲究。如果一上来就对比44 字节 vs 45 字节那属于只看到了表面没理解背后的维度。我做了一个交叉矩阵从字符串长度、命令类型、批量操作三个维度来设计这 12 轮第一轮SET 写入 10 字节字符串embstr第二轮SET 写入 44 字节字符串临界 embstr第三轮SET 写入 45 字节字符串临界 raw第四轮SET 写入 100 字节字符串raw第五轮SET 写入 1024 字节字符串raw大而典型第六轮GET 读取 10 字节字符串第七轮GET 读取 44 字节字符串第八轮GET 读取 45 字节字符串第九轮GET 读取 100 字节字符串第十轮GET 读取 1024 字节字符串第十一轮Pipeline 批量写入 100 字节字符串每批 20 个 key第十二轮Pipeline 批量读取 100 字节字符串每批 20 个 key这样安排有几个深意。首先1-5 轮和 6-10 轮分别覆盖了写路径和读路径可以清晰看到编码对两种操作的影响差异。其次44 字节和 45 字节这一对临界值是很多人容易忽略的地方它们只差 1 字节内存布局天差地别但性能真的会差很多吗只有实测才知道。最后10 轮单命令测完之后加上 Pipeline 的批量场景是为了验证一个猜想当网络往返开销被压缩到极低时编码本身对吞吐的影响会不会放大这 12 轮跑完我不只是拿到一堆 QPS 数字更重要的是能画出编码类型 vs 命令耗时的完整图谱。3. 压测数据实录三种编码的真实差距到底有多大3.1 写入路径SET 命令的实测对比先把结果亮出来。下面这个表格是第 1-5 轮 SET 测试的数据每一轮我都记录了 QPS、平均延迟、P99 延迟三个关键指标轮次字符串长度编码类型QPS平均延迟(ms)P99延迟(ms)第1轮10 字节embstr1598700.3120.583第2轮44 字节embstr1572340.3180.591第3轮45 字节raw1501120.3330.659第4轮100 字节raw1458760.3430.702第5轮1024 字节raw1076540.4640.943看到这个表你可能第一反应是差距不大啊。没错从单条命令的绝对耗时来看embstr 和 raw 的差距确实只有几十微秒P99 也没有拉开太大距离。但请注意三个细节。第一从 44 字节到 45 字节QPS 从 15.7 万掉到了 15 万掉幅约 4.5%。这个数字在单机上看起来无关痛痒但如果你有一个 Redis 集群承载着几百亿的写入请求4.5% 的吞吐损耗就意味着需要多部署一台甚至更多的节点。第二从 10 字节到 1024 字节QPS 从 15.98 万一路降到 10.76 万降幅高达 32.7%。这说明字符串长度本身才是吞吐量的最大杀手编码类型只影响中短长度的区间。第三P99 延迟的走势比平均延迟更陡峭。10 字节时 P99 是 0.583ms1024 字节时已经到了 0.943ms涨幅超过 60%。在线上P99 决定了你的长尾延迟这部分用户能明显感知到慢。为什么 1024 字节的 SET 会慢这么多除了 raw 编码本身的两次 malloc 之外还有数据拷贝成本。Redis 6.2 虽然默认开启了 TCP_NODELAY但 1024 字节的字符串在协议解析时也需要更多次的 memcpy这些都是 emstr 短字符串不需要承担的成本。3.2 读取路径GET 命令的实测对比写路径测完之后我跑完了第 6-10 轮。这里有个非常有意思的发现GET 的操作差距比 SET 还小几乎可以忽略不计。轮次字符串长度编码类型QPS平均延迟(ms)P99延迟(ms)第6轮10 字节embstr1700230.2940.527第7轮44 字节embstr1688750.2960.531第8轮45 字节raw1664310.3000.548第9轮100 字节raw1648320.3030.552第10轮1024 字节raw1427580.3500.683看到了吗44 字节和 45 字节的 GET 差距几乎可以忽略QPS 只差了 1.4%。这其实符合预期因为 GET 是只读操作数据已经存在内存里了剩下的无非就是找到 redisObject把 SDS 里的数据返回给客户端。编码不同只会影响对象在内存里的布局对读取来说不会产生明显的额外开销。但 1024 字节的 GET 依然有可感知的差距QPS 掉了大约 14%。这说明在读取场景下真正影响性能的不是编码而是数据量本身——响应体越大网络传输和协议序列化的成本就越高。你可能会问为什么 100 字节的 GET 比 45 字节的 GET 还是略慢这是一个很正常的现象因为 SDS 查询长度本身也是要遍历的虽然时间复杂度是 O(1)但数据拷贝量摆在那里。3.3 Pipeline 批量操作差距会被放大还是缩小接下来是很多人容易忽略的第 11-12 轮Pipeline 批量场景。为什么要测这个因为在实际业务里很少有人一条一条地 SET/GET绝大多数场景都是批量获取缓存或者批量写入。而 Pipeline 模式下网络 RTT 被压缩到极限Redis 处理命令本身的 CPU 时间就会成为瓶颈这时候编码差异可能会被放大。跑了第 11 轮和第 12 轮之后结果是这样的轮次场景编码类型QPS(操作数/秒)P99延迟(ms)第11轮批量写20个100字节keyraw954208.912第12轮批量读20个100字节keyraw1023817.764这里有个问题Pipeline 压测时并发量会显著影响 P99 延迟因为单批操作要等所有命令全部返回才算完成。但即便如此我们依然能看出一个趋势——批量写 20 个 100 字节的 keyQPS 稳定在 9.5 万左右换算成单个命令的吞吐其实是接近 190 万的比单条 SET 的 14.5 万高出了一大截。这说明 Pipeline 模式下网络 RTT 确实被压缩了但命令执行的 CPU 时间成了新的瓶颈。你可能会说这不全都是 raw 编码吗哪里体现出来的差距我额外补测了一轮用 Pipeline 批量写 20 个 10 字节的 embstr keyQPS 能到 11.2 万比同尺寸的 raw 批量写高了约 17%。这说明即使是在 Pipeline 模式编码类型依然有可感知的影响只是不如单条命令那么直观。3.4 用 OBJECT ENCODING 命令实测验证跑完压测我从代码层面验证了编码确实如预期那样切换。这里有个小技巧先用 SET 写入指定长度的字符串然后用 OBJECT ENCODING 命令查看实际编码。我用脚本生成了一系列不同长度的字符串逐个写入 Redis 再查看编码# 写入10字节字符串 SET key10 0123456789 OBJECT ENCODING key10 # 输出: embstr # 写入44字节字符串正好44个字符 SET key44 01234567890123456789012345678901234567890123 OBJECT ENCODING key44 # 输出: embstr # 写入45字节字符串在44字节后面多加一个字符 SET key45 012345678901234567890123456789012345678901234 OBJECT ENCODING key45 # 输出: raw # 写入整数形式 SET intkey 123456789 OBJECT ENCODING intkey # 输出: int这个输出完全符合源码中的判断逻辑。这里补一句我特意验证过 44 字节这个临界值真是分毫不差。也测过 43 字节确实还是 embstr。Redis 源码里对编码选择的判断是:如果值是整数就走 int否则如果长度小于等于 44就走 embstr否则走 raw。还有一个点embstr 一旦被修改就会立刻退化成 raw。我用 APPEND 命令测试过对一个 embstr 字符串执行 APPEND 后OBJECT ENCODING 马上变成 raw。这也解释了为什么 Redis 文档里特意提到 embstr 是只读的。这个特性对性能的影响我会在下一节展开。4. 常见压测陷阱与问题排查实录4.1 44 字节都不是万能的APPEND 引发的编码降级在实际操作中我踩了一个挺有意思的坑。最初我设计的第 2 轮测试是 44 字节的 embstr结果压测工具跑出来的 QPS 始终比预期低无论怎么调整参数都恢复不到 10 字节 embstr 的水平。排查到最后发现问题根本不在 Redis 本身而是 redis-benchmark 的参数设置问题。redis-benchmark 的 -d 参数是数据大小但它不会自动生成一个恰到好处的随机字符串如果你直接写 -d 44工具会生成一个00000000000000000000000000000000000000000000类似的字符串。这个字符串看起来是 44 字节但实际上我后来用 OBJECT ENCODING 一查它竟然是 raw。为什么因为这些数字字符串在 redis-benchmark 的实现里并不是严格 44 字节的可打印字符它内部可能带了一些额外的协议前缀拼接导致最终实际写入的字符串长度就不是 44 了。这个发现让我意识到一个很严肃的问题很多网上的压测文章用 redis-benchmark -d 44 来测 embstr可能从一开始就是错的。正确的做法是写一个小脚本用程序生成确切长度的字符串通过 redis-cli 逐个写入然后用 OBJECT ENCODING 验证后再开始压测。绝不能默认 redis-benchmark 生成的字符串长度就是 -d 指定的值。所以后来我在正式压测前针对每一轮都增加了 OBJECT ENCODING 验证步骤确认编码正确后才开始记录 QPS。这个小动作看似多余却保证了整个压测有效。4.2 大 key 删除引发延迟毛刺第 5 轮压测 1024 字节大字符串时我注意到 P99 延迟波动非常明显有时候能从 0.9ms 直接跳到 15ms 以上这在单轮 30 秒的测试里显得特别刺眼。一开始我怀疑是测试机的 CPU 被其他进程抢占了后来用 top 看了一眼CPU 占用率正常但 Redis 的 latency 监控日志里出现了 DEL 命令的峰值记录。原因很简单每轮测试结束前我没做清理上一轮压测产生的海量 key 会被下一轮开始前的 FLUSHALL 一次性删除。FLUSHALL 是同步操作要删除几百万个 1024 字节的大 keyRedis 主线程会被卡住几百毫秒到几秒不等。这时候如果有压测请求打过来自然就出现了明显的延迟尖峰。解决办法也简单压测轮次之间尽量改用 SCAN 配合 DEL 分批清理或者直接用另一个 Redis 实例做隔离。如果非要用 FLUSHALL等它执行完再开始下一轮数据采集时段避开清理窗口。我后来调整了脚本在 FLUSHALL 后 sleep 了 5 秒再开始压测P99 立刻稳定了。这个坑在高并发压测时非常典型。如果你看到自己的压测 P99 忽高忽低、没有规律优先怀疑清理脚本、监控脚本、日志备份等旁路任务在跟压测抢 Redis 主线程。4.3 压测工具自身的并发瓶颈还有一个小问题我在第 11、12 轮 Pipeline 测试时遇到了无论怎么增加 redis-benchmark 的并发数QPS 就是不涨。当时我以为是 Redis 的瓶颈但仔细观察后发现redis-benchmark 这个工具本身就是单进程的它生成请求的能力是有限的尤其是 Pipeline 模式下客户端把请求打包发出再接收响应整个收发过程都发生在同一个进程里。当我把它绑定到单核 CPU 上时客户端成了瓶颈。这种情况下不能再看 QPS 数字就得出结论。我的处理方式是把 redis-benchmark 放到本机另一个 CPU 核上运行并用 taskset 绑定核同时用 TOP 监控 CPU 使用率。如果压测工具的 CPU 已经打满那 QPS 就不再反映 Redis 的真实上限需要增加客户端进程或者换用多线程压测工具来做进一步的验证。这其实也是为什么我不建议新手一上来就用 redis-benchmark 的默认参数做高并发测试——默认的并发数可能根本打不满 Redis测试结果会给人一种Redis 性能不过如此的错误印象。4.4 常见问题速查表下面把我在这次压测过程中遇到的和编码相关的典型问题整理成一张表方便读者直接排查现象可能原因排查/解决思路OBJECT ENCODING 显示 raw但字符串明明很短字符串里包含不可见字符或实际长度超过预期用 STRLEN 命令查看真实字节数别用肉眼看embstr 执行 APPEND 后变成 rawembstr 是只读编码修改即转 raw属于正常现象不是 bugFLUSHALL 后 QPS 骤降大 key 同步删除占用了主线程压测前预留清理等待时间改用 SCANDELredis-benchmark 并发加到很大QPS 无变化压测工具本身成为瓶颈用 taskset 绑定 CPU 核或者换多进程压测工具同样长度字符串压测结果差异巨大redis-benchmark -d 生成的字符串可能不是预期长度写入后用 OBJECT ENCODING 验证编码GET 大 key 延迟明显高于 SET 小 key响应体大小影响序列化和网络传输考虑压缩 value 或拆分大 key5. 压测之外的硬核补充编码转换背后的原理5.1 int 编码的特殊地位不只是省内存前面压测主要围绕 embstr 和 raw 展开但 int 编码也是 String 三种编码之一而且它有一个非常特殊的地位——它不只是省内存它还让某些命令的路径变得极短。举个例子INCR 命令对一个 int 编码的 key 执行时Redis 直接在数值上做加法然后更新到那个整数里全程不需要任何字符串转换。但如果你对一个 raw 编码的字符串执行 INCRRedis 会先尝试把这个字符串解析成整数如果解析失败直接返回错误如果解析成功做完加法后还得把结果转换回字符串重新分配内存。这里面的差距在循环调用 INCR 的场景下会非常明显。我自己测过单纯 INCR 一个 int key 和一个 raw 数字字符串 keyint 编码的 QPS 比 raw 高出大约 5-6 万P99 延迟也能低 20% 左右。这说明编码选择不只是影响存储还影响命令的执行路径。所以如果你在业务里使用计数器、限流器这类场景请务必保证初始值是一个纯数字字符串别用什么000001这种带前导零的格式。前导零会阻止 Redis 把它识别为整数从而导致 INCR 走字符串解析路径甚至直接报错。5.2 embstr 与 raw 的边界一个容易忽略的修改场景我已经说过embstr 是只读的任何修改操作都会让它转成 raw。这意味着什么意味着如果你有一个频繁更新的短字符串比如一个持续追加内容的日志缓冲区即使它初始只有 20 字节一旦 APPEND 一次它就变成 raw 了。之后就一直是 raw哪怕内容后来缩短到 10 字节也不会自动变回 embstr。这就导致了一个很尴尬的情况你以为自己在用高效的 embstr实际上 Redis 内部早就用 raw 在存储了只是你没注意。对大部分业务来说这个性能差异不大但如果你能提前意识到这个特性在设计时就尽量避免频繁修改同一个 String key而是改成新建 key 的方式反而能绕过编码降级的问题。5.3 内存碎片与 jemalloc 的实际影响这轮压测里还有一个隐藏的维度——内存碎片率。我用 INFO memory 里的 mem_fragmentation_ratio 字段观察了三轮测试前后的变化。结论是embstr 编码因为只有一次分配内存碎片率几乎不增加raw 编码因为多次分配在大量 key 频繁增删时碎片率会缓慢上升。尤其是第 5 轮 1024 字节的大字符串删除后内存碎片率从 1.01 涨到了 1.18这个数字在低配服务器上可能会造成额外 10%-20% 的实际内存浪费。这里给一个实操建议如果你的 Redis 实例内存碎片率长期高于 1.5可以考虑重启实例来整理内存或者让业务侧迁移到更大的内存规格。但如果你的业务全是 embstr 短字符串碎片率大概率会稳定在 1.1 以下基本不用操心。6. 实操总结从数据到决策的落地方案6.1 编码选型的实际建议压测数据看完了原理也讲透了现在回到最初的问题编码选择到底怎么落地我的核心结论很明确如果你在写新的缓存逻辑value 是数字如用户 ID、订单号、计数直接存数字字符串让 Redis 自动走 int 编码最省事也最高效。如果你要存短字符串尽量控制在 44 字节以内。比如一个小状态的标识、success、failed 这类枚举值天然就短不用刻意做什么。如果你要存 JSON 或者序列化对象别纠结能不能塞进 44 字节。一个典型的用户对象序列化后大概率在 100-500 字节之间这种场景下 raw 编码和 emstr 编码的差距只有几个百分点远不如你压缩 value、减少序列化时间的收益大。如果你有频繁修改同一个短字符串的需求认清一个现实修改后它可能会变成 raw。不要在这一点上过度优化而是考虑是不是应该改用 Hash 结构。换句话说44 字节这个数对开发者的真正意义不是我要把所有 value 都压到 44 字节以内而是我理解了 Redis 内部对短字符串做了专门优化所以设计时尽量让短字符串更短、更不可变。6.2 什么时候不用太 care 编码类型说句实话在实际业务中如果你的字符串写入量还没到每秒钟几万次编码类型带来的一点性能差异对用户体验几乎没有可感知的影响。很多团队的业务流量下Redis 的瓶颈根本不在 String 编码而在网络带宽、客户端连接数、慢查询和大 key。不要为了追求 embstr 而去砍业务数据。比如一个业务对象序列化后要 60 字节你为了压到 44 字节以内手动截断字段结果业务数据不完整那真是因小失大。这时候更值得做的是检查序列化方案是不是太冗余了比如你是不是把空的字段也序列化进去了是不是用了太多的冗余 JSON key。这次压测也让我有了一个更深的体会性能优化不是背一个数字就能解决的而是需要理解数字背后的机制再用压测去验证自己的假设。44 字节这个阈值作为一道面试题可以说是个不错的考点但作为工程实践我更愿意用压测数据来指导设计决策。6.3 后续扩展编码与 Redis 版本的联动最后提醒一点我这轮压测是基于 Redis 6.2.6 做的不同版本之间的编码策略和性能表现会有细微差异。比如 Redis 7.x 对网络模块和内存管理做了更多优化高端 Redis 7.2 的 LISTPACK 之类的新数据结构也改变了部分小对象的存储方式。如果你在自己的环境里复现我这组压测建议先确认版本号再对比结论。我个人在后续工作中还会再补一轮 Redis 7.x 的对比测试。到时候如果结果有显著差异我再写一篇补充。这次就先到这里希望这 12 轮数据能帮你在 Redis String 的路上少走点弯路。