首页 > redis > redis中底层的编码转换
2017
05-22

redis中底层的编码转换

编码转化
Redis 使用对象(redisObject)来表示数据库中的键值,当我们在 Redis 中创建一个键值对时,至少创建两个对象,一个对象是用做键值对的键对象,另一个是键值对的值对象。
例如我们执行 SET MSG XXX 时,键值对的键是一个包含了字符串“MSG”的对象,键值对的值对象是包含字符串”XXX”的对象。
redisObject 的结构如下:
typedef struct redisObject{
    //类型
   unsigned type:4;
   //编码
   unsigned encoding:4;
   //指向底层数据结构的指针
   void *ptr;
    //...
 }robj;
其中 type 字段记录了对象的类型,包含字符串对象、列表对象、哈希对象、集合对象、有序集合对象。
当我们执行 type key 命令时返回的结果如下:

图片.png

ptr 指针字段指向对象底层实现的数据结构,而这些数据结构是由 encoding 字段决定的,每种对象至少有两种数据编码:
图片.png
细心的读者可能注意到,list、hash、zset 三个键使用的是 ziplist 压缩列表编码,这就涉及到了 Redis 底层的编码转换。
上面介绍到,ziplist 是一种结构紧凑的数据结构,当某一键值中所包含的元素较少时,会优先存储在 ziplist 中,当元素个数超过某一值后,才将 ziplist 转化为标准存储结构,而这一值是可配置的。
1、String 对象的编码转化
String 对象的编码可以是 int 或 raw,对于 String 类型的键值,如果我们存储的是纯数字,Redis 底层采用的是 int 类型的编码,如果其中包括非数字,则会立即转为 raw 编码:
127.0.0.1:6379> set str 1
OK
127.0.0.1:6379> object encoding str
"int"
127.0.0.1:6379> set str 1a
OK
127.0.0.1:6379> object encoding str
"raw"
2、List 对象的编码转化
List 对象的编码可以是 ziplist 或 linkedlist,对于 List 类型的键值,当列表对象同时满足以下两个条件时,采用 ziplist 编码:
  • 列表对象保存的所有字符串元素的长度都小于 64 字节

  • 列表对象保存的元素个数小于 512 个

如果不满足这两个条件的任意一个,就会转化为 linkedlist 编码。
注意:这两个条件是可以修改的。
在 redis.conf 中:
list-max-ziplist-entries 512
list-max-ziplist-value 64
3、Set 类型的编码转化
Set 对象的编码可以是 intset 或 hashtable,intset 编码的结果对象使用整数集合作为底层实现,把所有元素都保存在一个整数集合里面。
127.0.0.1:6379> sadd set 1 2 3
(integer) 3
127.0.0.1:6379> object encoding set
"intset"
如果 set 集合中保存了非整数类型的数据时,Redis 会将 intset 转化为 hashtable:
127.0.0.1:6379> sadd set 1 2 3
(integer) 3
127.0.0.1:6379> object encoding set
"intset"
127.0.0.1:6379> sadd set a
(integer) 1
127.0.0.1:6379> object encoding set
"hashtable"
当 Set 对象同时满足以下两个条件时,对象采用 intset 编码:
  • 保存的所有元素都是整数值(小数不行)

  • Set 对象保存的所有元素个数小于 512 个

不能满足这两个条件的任意一个,Set 都会采用 hashtable 存储。
注意:第两个条件是可以修改的。
在 redis.conf 中:
set-max-intset-entries 512
4、Hash 对象的编码转化
Hash 对象的编码可以是 ziplist 或 hashtable,当 Hash 以 ziplist 编码存储的时候,保存同一键值对的两个节点总是紧挨在一起,键节点在前,值节点在后:
图片.png
当 Hash 对象同时满足以下两个条件时,Hash 对象采用 ziplist 编码:
  • Hash 对象保存的所有键值对的键和值的字符串长度均小于 64 字节

  • Hash 对象保存的键值对数量小于 512 个

如果不满足以上条件的任意一个,ziplist 就会转化为 hashtable 编码。
注意:这两个条件是可以修改的。
在 redis.conf 中:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
5、Zset 对象的编码转化
Zset 对象的编码可以是 ziplist 或 zkiplist,当采用 ziplist 编码存储时,每个集合元素使用两个紧挨在一起的压缩列表来存储。
第一个节点存储元素的成员,第二个节点存储元素的分值,并且按分值大小从小到大有序排列。

图片.png

在 redis.conf 中:

zset-max-ziplist-entries 128
zset-max-ziplist-value 64m
思考:Zset 如何做到 O(1) 复杂度内元素并且快速进行范围操作?
Zset 采用 skiplist 编码时使用 zset 结构作为底层实现,该数据结构同时包含了一个跳跃表和一个字典。
其结构如下:
typedef struct zset{
   zskiplist *zsl;
   dict *dict;
}
Zset 中的 dict 字典为集合创建了一个从成员到分值之间的映射,字典中的键保存了成员,字典中的值保存了成员的分值,这样定位元素时时间复杂度是 O(1)。
Zset 中的 zsl 跳跃表适合范围操作,比如 ZRANK、ZRANGE 等,程序使用 zkiplist。
另外,虽然 Zset 中使用了 dict 和 skiplist 存储数据,但这两种数据结构都会通过指针来共享相同的内存,所以没有必要担心内存的浪费。


本文》有 0 条评论

留下一个回复