String类型存小整数时,若满足64位有符号整数范围且未加引号、未被字符串操作污染,则采用int编码,直接以8字节long存储,总开销约24字节;否则退化为embstr或raw编码,底层使用SDS结构,内存占用显著增加。

String类型存小整数时,实际用的是SDS结构,不是直接存数字
当你执行 SET counter 123,Redis 并不会把 123 当作整数原生存储——除非它满足 int 编码条件。而是否触发 int 编码,取决于值本身是不是「64位有符号整数」且未被强制转为字符串。如果你写的是 SET counter "123"(带引号),那 Redis 一定走 raw 或 embstr 编码,底层是 SDS:至少要存 len(4字节)、alloc(4字节)、结尾 \0(1字节)+ 实际数据(3字节),再加上 redisObject 的 16 字节固定开销,总开销轻松超 30 字节。而纯整数 123(不带引号)才可能走 int 编码,只占约 24 字节。
int 编码的触发条件很具体
不是所有整数都能进 int 编码。必须同时满足:
- 值是纯数字,且能被
long表示(即 ≥LONG_MIN且 ≤LONG_MAX) - 命令中没加引号,例如
SET key 123✅,而不是SET key "123"❌ - 未经过字符串类操作污染,比如后续执行了
APPEND或GETRANGE,会强制转成 embstr/raw - 共享对象池范围(0–9999)内的整数,还会复用
shared.integers数组,进一步省空间
一旦不满足任一条件,Redis 就退化到 embstr(≤44 字节)或 raw(>44 字节),内存占用立刻翻倍。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
embstr 和 raw 编码的实际开销差异
即使你只存一个 3 位数,比如 123,如果因引号或操作被识别为字符串,就会走 embstr:
- embstr:
redisObject(16B) + 连续分配的 SDS(9B 头部 + 3B 数据 + 1B\0) = 至少 29 字节 - raw:
redisObject(16B) + 单独 malloc 的 SDS(9B 头部 + 数据 +\0),且两块内存不连续 → 更易碎片化,GC 成本更高 - 对比 int 编码:16B
redisObject+ 8B 直接存值 = 24B,无额外指针、无 SDS 头部、无字符串语义开销
也就是说,"123" 比 123 多花至少 5 字节,还失去共享对象复用机会;而存 1 亿个,就多占近 500MB 内存。
容易被忽略的隐式字符串转换
很多业务代码看似在存整数,实则悄悄触发了字符串编码:
- Java 客户端用
jedis.set("key", String.valueOf(123))→ 传入的是"123"字符串对象 - Python 中
r.set('key', 123)可能自动转 str(取决于客户端实现),建议显式用r.set('key', 123, encoding=None)或检查协议层是否发整数 - 任何对 key 执行过
INCRBYFLOAT、GETRANGE、STRLEN后,即使原先是 int 编码,也会被降级为 embstr - 使用 Lua 脚本时,
redis.call("SET", KEYS[1], ARGV[1])若ARGV[1]是 number 类型,Redis 通常能保持 int 编码;但若脚本里拼接过字符串(如ARGV[1] .. "x"),结果必为字符串
真正省内存的关键,不是“用 String 存整数”,而是“让 Redis 知道你存的就是整数”——这依赖于值的原始类型、传输方式和后续操作链的洁净度。稍有不慎,int 编码就失效,开销立马回归 SDS 老路。

















