bigywater.tech
Redis · 12

String 的 EMBSTR 与 RAW

验证44字节和45字节字符串的编码差异,以及哪怕只有2个字符的短字符串,一次APPEND也会立刻变成raw编码。

44/45字节
embstr转raw的精确边界
APPEND强制转换
与字符串最终长度无关
一次性 vs 可扩容
embstr单块内存 vs raw独立可扩容内存

做的事:44字节和45字节的字符串,各测一次

String类型除了int编码,还有两种存"真正字符串"的方式:embstr和raw。

操作SET s44 "aaaa...(44个a)" OBJECT ENCODING s44 SET s45 "aaaa...(45个a)" OBJECT ENCODING s45

验证:44字节还是embstr,45字节立刻变raw

边界精确卡在44这个数字上。

结果STRLEN s44 → 44 OBJECT ENCODING s44 → :embstr STRLEN s45 → 45 OBJECT ENCODING s45 → :raw

精确验证:44字节是embstr能装下的上限,45字节立刻变成raw。embstr(embedded string)是把字符串内容和对象头一次性分配在同一块连续内存里,适合短字符串,创建时只需要一次内存分配;raw则是对象头和字符串内容分开存储、独立分配,适合长字符串或者需要频繁修改的字符串。

验证:哪怕只有2个字符,一次APPEND也立刻变raw

字符串长度明明远小于44,为什么会提前转换。

结果SET short "hi" OBJECT ENCODING short → :embstr APPEND short "!" OBJECT ENCODING short → :raw(长度才3,远小于44,却已经不是embstr了)

原因跟长度没关系:embstr的内存布局是不可修改的——对象头和字符串数据分配在同一块内存里,一旦要追加内容,没法在原地"扩容"这块内存(对象头挡在前面),只能整个重新分配一块新内存。Redis干脆规定:任何会修改字符串内容的操作(APPEND、SETRANGE等),一律先把embstr转成raw再操作——raw的对象头和内容是分开的,扩容内容不需要动到对象头那部分。

这也是为什么"看起来很短的字符串"有时候OBJECT ENCODING显示的是raw而不是embstr——只要执行过一次修改型操作,哪怕结果依然很短,它也回不去embstr了,只有重新SET才会按当前长度重新判断一次编码。

分步演示:embstr的边界和的强制转换时机

每一步都是本文上面用redis-cli跑出来的原始结果。

44/45字节的精确边界,以及APPEND触发的强制转换
这一步新验证的字符串

    本文用到的工具

    • 本机 Redis 8.10.1 单节点
    • OBJECT ENCODING / STRLEN / APPEND:验证embstr/raw边界与转换时机
    京ICP备2026067502号-1京公网安备11010502063487号