Redis中字符串的编码

  |  

摘要: Redis 中字符串的编码

【对算法,数学,计算机感兴趣的同学,欢迎关注我哈,阅读更多原创文章】
我的网站:潮汐朝夕的生活实验室
我的公众号:算法题刷刷
我的知乎:潮汐朝夕
我的github:FennelDumplings
我的leetcode:FennelDumplings



Redis 五大数据类型 String、Hash、List、Set、sorted set,每种数据类型都提供了最少两种内部的编码格式,Redis 会根据数据量自适应地选择较优化的内部编码格式

想查看某个键的内部编码格式,可以使用 OBJECT ENCODING keyname 指令来进行,例如

Redis 的每个键值内部都是使用一个 C 语言结构体保存的,代码在 src/server.h 如下:

type:表示键值的数据类型,包括 String、List、Set、ZSet、Hash
refcount:表示该键值被引用的数量,即一个键值可被多个键引用
encoding:表示键值的内部编码方式,这是我们在这里关心的内容,源码依然在 src/server.h,取值有如下几种:

对于此处关心的字符串编码,Redis 中字符串对象的编码可以是 int,raw 或者 embstr 中的某一种。

1
2
3
#define OBJ_ENCODING_RAW 0 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
  • int 编码:保存long 型的64位有符号整数
  • embstr 编码:保存长度小于44字节的字符串
  • raw 编码:保存长度大于44字节的字符串

以下对实际情况做测试

1
2
3
4
5
6
正常数字: 123
长度小于20的数字: 1234567890123456789
长度大于等于20的数字: 12345678901234567890
正常字符串:abc
长度小于等于44的字符串:abcdefghijklamopqrstuvwxyzabcdefghijklamopqr
长度大于44的字符串:abcdefghijklamopqrstuvwxyzabcdefghijklamopqrs

可以看到:Redis 内部会根据用户给的不同键值而使用不同的编码格式,而变换格式的策略,我们作为用户是可以知道的。

Redis 是使用 SDS(简单动态字符串) 结构体来存储字符串(前面截图的代码中 608 行也有想关注释 #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */),在代码 src/sds.h 里定义了 5种 SDS结构体:

在五种定义中,这些字段名是一样的,只有类型稍有不同,见代码。结构体中的字段名如下:

  • len:字符串的长度(实际使用的长度)
  • alloc:分配内存的大小
  • flags:标志位,低三位表示类型,其余五位未使用
  • buf:字符数组

在此前试验中涉及到的三种编码:INT, EMDSTR, RAW。下面看一下它们在 Redis 中是如何存储的。

(1) INT 编码

当字符串键值的内容可以用一个 64 位有符号整形 来表示时,Redis 会将键值转化为 long 型存储,对应 OBJ_ENCODING_INT 编码。

OBJ_ENCODING_INT 编码类型的对象的内存布局(以”123”为例):

OBJ_ENCODING_INT 编码类型内存布局

Redis 启动时会预先建立 10000 个分别存储 0~9999 的 redisObject 变量作为共享对象,这就意味着如果 set 字符串的键值在 0~10000 之间的话,则可以直接指向共享对象而不需要再建立新对象,此时键值是不占空间的。

例如,三个键 key1, key2, key3, 其值均 set 为 100

1
2
3
set key1 100
set key2 100
set key3 100

实际的内存布局是这样:

OBJ_ENCODING_EMBSTR 编码类型内存布局

此过程可以与 src/object.c 中的以下的源码对照:

(2) EMBSTR 编码

we can represent this string as a long integer
如果不能将字符串表示为 LONG 型整数的话,Redis 在保存长度小于等于 44 字节的字符串时会采用 OBJ_ENCODING_EMBSTR 编码方式,在保存大于 44 字节的字符串时用 OBJ_ENCODING_RAW 编码。对照 src/object.c 的代码:

OBJ_ENCODING_EMBSTR 编码类型的对象的内存布局(以”abc”为例):

EMBSTR 字面意思是嵌入式的String。从内存布局上看: sds 结构体与其对应的 redisObject 对象分配在同一块连续的内存空间,sds 确实是嵌入在 redisObject 对象之中。对照 src/object.c 中的代码:

(3) RAW 编码

对于长度大于 44 的超长字符串,Redis 会用 OBJ_ENCODING_RAW 编码,OBJ_ENCODING_EMBSTR 编码不同是此时动态字符串 sds 的内存与其依赖的 redisObject 对象的内存不连续。

实例如下图:

OBJ_ENCODING_RAW 编码类型内存布局


Share