个人博客

redis数据类型的底层实现

一、简介

redis是一种key-value型的内存数据库,它所有的key都是字符串,而value常见的数据类型有五种:string、list、set、zset、hash。redis的这些数据结构, 在底层都是使用redisObject来进行表示。redisObject中有三个重要的属性, 分别是type、 encoding 和 ptr。

二、对象类型与编码

redis的key和value 都是对象。redis对象有五种:

类型常量 对象的名称
REDIS_STRING 字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET 有序集合对象

key总是字符串对象,value有可能是以上五种。

对象的内部结构

typedef struct redisObject {
    // 类型
   unsigned type:4;
   // 编码
   unsigned encoding:4;
   // 指向底层实现数据结构的指针
   void *ptr;
   ......
}

对象的 ptr 指针指向对象的底层实现数据结构, 而这些数据结构由对象的 encoding 属性决定。

encoding 记录了这个对象使用了什么数据结构作为对象的底层实现。

type encoding 对象
REDIS_STRING REDIS_ENCODING_INT 使用整形值实现的字符串对象
REDIS_STRING REDIS_ENCODING_EMBSTR 使用embstr编码的简单动态字符串实现的字符串对象
REDIS_STRING REDIS_ENCODING_RAW 使用简单动态字符串实现的字符串对象
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用压缩列表实现的列表对象
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用双端链表的列表对象
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用压缩列表实现的哈希对象
REDIS_HASH REDIS_ENCODING_HT 使用字典实现的哈希对象
REDIS_SET REDIS_ENCODING_INTSET 使用证书集合实现的集合对象
REDIS_SET REDIS_ENCODING_HT 使用字典实现的集合对象
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用压缩列表实现的有序集合对象
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳跃表和字典实现的有序集合对象

三、string

字符串对象的 encoding 有三种,分别是:int、raw、embstr。

  • 1、字符串对象,如果保存整数。void则指向long,编码设置为int。
  • 2、如果是字符串会根据字符串长度判断编码方式。(sds len属性)
  • 3、对于浮点数的保存也是使用sds,当作字符串值来保存的。编码类型为embstr或raw。

长度大于39个字节,那么数据结构为SDS,编码为raw。

picture

如果是字符串,且小于39个字节,将使用embstr编码保存。

embstr是一种优化编码。虽然也使用SDS数据结构,但它只调用一次内存分配,比raw少调用一次。它调用一次内存分配,直接分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构。

picture

四、hash

哈希对象的编码有两种,分别是:ziplist、hashtable。

当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
  • 哈希对象保存的键值对数量小于 512 个;

数量小于 512,且键值对长度都小于 64 字节,使用ziplist(压缩列表)存储

picture

picture

key和value是先后被push到表尾的。key与value紧挨着,同时每对key-value也紧挨着。(key与value都是StringObject)

数量不小于 512或键值对长度有达到 64 字节,使用的是hashtable编码。

picture

五、list

列表的 encoding 有两种,分别是:ziplist、linkedlist。

当列表对象可以同时满足以下两个条件时, 列表对象使用 ziplist 编码:

  • 列表对象保存的所有字符串元素长度小于64字节;
  • 列表对象保存的元素数量小于512个;

长度小于 512,且所有元素的长度都小于 64 字节,使用ziplist编码

picture

长度不小于 512或元素的长度有达到 64 字节,使用linkedlist编码

picture

六、set

集合的 encoding 有两种,分别是:intset、hashtable。

当集合对象可以同时满足以下两个条件时, 集合对象使用 intset 编码:

  • 集合对象保存的所有元素都是整数值;
  • 集合对象保存的元素数量不超过 512 个;

数量小于 512,且所有元素都是整数值,使用intset编码

picture

数量不小于 512或所有元素不全是整数值,使用hashtable编码

picture

key(StringObject)保存元素值,value为NULL。

七、zset

zset的编码有两种,分别是:ziplist、skiplist。

当有序集合对象可以同时满足以下两个条件时, 有序集合对象使用 ziplist编码:

  • 有序集合对象保存的所有元素的长度都小于 64 字节;
  • 有序集合对象保存的元素个数小于128个;

个数小于 128,且所有元素的长度都小于 64 字节,使用ziplist编码

picture

个数不小于 128或元素存在长度达到 64 字节,使用skiplist编码

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表。

picture

typedef struct zset {
    zskiplist *zs1
    dict *dict
}

zsl 跳跃表按分值从小到大保存了所有集合元素。

每个跳跃表节点都保存了一个集合元素: 跳跃表节点的 object 属性保存了元素的成员, 而跳跃表节点的 score 属性则保存了元素的分值。

dict 字典为有序集合创建了一个从成员到分值的映射。

相关标签
回到顶部