REDIS系列之五大对象原理

Posted by Tango on December 1, 2018

Redis并没有采用上文介绍的底层数据结构实现键值对数据库,而是基于底层数据结构实现一套对象系统,包括字符串对象、链表对象、哈希对象、集合对象、排序集合对象。而且,每个对象的底层实现至少存在两种,针对不同的应用场景可以选择不同的实现方式,从而提高效率。

1.对象系统

Redis是key-value数据库,每创建一个键值对就会创建两个对象,即一个键对象,一个值对象。Redis中默认键是字符串对象,值是redisObject对象。其结构如下:

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

其内存结构如图所示: 其中类型type如下:

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

encoding如下:

编码常量 底层数据结构
REDIS_ENCODING_INT long型整数
REDIS_ENCODING_EMBSTR embstr编码简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合
REDIS_ENCODING_SKIPLIST 跳跃表和字典

下表表示不同类型和编码的对象:

类型 编码 对象
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 跳跃表和字典实现的有序集合对象

从上表可以看出任意一种对象都至少有两种实现方式OBJECT ENCODING 命令显示对象的编码类型

   redis>set msg "hello world"     
   redis>OBJECT ENCODING msg
         "embstr" 

2.字符串对象

字符串对象有intrawembstr三种编码方式,其底层数据结构三个原则:

  • 如果字符串对象保存的是整数值,并且这个整数值可以用long型数据表示,则底层数据采用int型编码
  • 如果字符串对象保存的是一个字符串值,并且这个字符串的长度大于32个字节,那么字符串对象将使用一个简单动态字符串(SDS)保存,即编码方式采用raw.
  • 如果字符串对象保存的是一个字符串值,并且这个字符串的长度小于于32个字节,那么字符串对象将使用embstr字符串保存(连续一块内存),即编码方式采用embstr. 如图是三种编码方式内存结构: 字符串常用命令:
命令 功能 说明
SET 保存字符串值  
Get 获取字符串值  
APPEND 追加字符串到末尾  
INCRBYFLOAT 对浮点数进行加法运算 Increase By Float
INCRBY 对整数进行加法运算  
DECRBY 对整数进行减法运算  
STRLEN 返回字符串长度  
SETRANGE 将字符串特定索引上单值设置为给定的字符  
GETRANGE 直接取出并返回字符串指定索引上的字符  

3.列表对象

列表对象ziplistlinkedlist两种编码方式,其底层数据结构采用编码方式的原则:

  • 列表对象保存的 所有字符串元素的长度都小于64字节时,采用ziplist编码
  • 列表对象保存的元素数量小于512个时,采用ziplist编码
  • 不满足上述条件其他情况,都采用linkedlist编码方式。

其内存结构如下图所示:
列表常用命令如下:

命令 功能 说明
LPUSH 将元素压入列表的表头 Left Push
RPUSH 将元素压入列表的表位 Right Push
LPOP 表头元素弹出并删除 Left Pop
RPOP 表尾元素弹出并删除 Right Pop
LINDEX 返回指定下标的节点保存的元素 List Index
LLEN 返回列表的长度 List Length
LINSERT 插入元素到列表指定位置 List Insert
LREM 遍历列表,删除包含给定元素的节点 L Remove
LTRIM 删除压缩列表中不在指定索引范围内的节点  
LSET 将指定位置的元素保存的值替换为新节点值 List Set

4.哈希对象

哈希对象采用ziplisthashtable两种编码方式实现。其底层数据结构采用的编码方式的原则:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节,采用ziplist编码方式
  • 哈希对象保存的键值对数量小于512个,采用ziplist编码
  • 满足上述两个条件则采用hashtable编码方式

如图是两种编码方式内存结构:
哈希对象常用命令如下:

命令 功能 说明
HSET 将新节点加入到哈希对象 Hash Set
HGET 在哈希对象中查找指定键对应的值 Hash Get
HEXISTS 判断指定的键值对是否存在 Hash Exists
HDEL 将指定的键值对删除 Hash Delete
HLEN 返回哈希对象中键值对的数量 Hash Length
HGETALL 返回哈希对象中所有的键和值 Hash Get All

5.集合对象

集合对象采用intset、hashtable两种编码方式。其底层数据结构采用的编码方式的原则:

  • 集合对象保存的所有元素都是整数值时,采用intset编码方式;
  • 集合对象保存的元素数量不超过512个时,采用intset编码方式
  • 不满足上述两个条件采用hashtable编码方式

如图是两种编码方式内存结构: 集合对象常用命令如下:

命令 功能 说明
SADD 将所有元素添加到集合里 Set Add
SCARD 返回集合中包含元素的数量 Set Card
SISMEMBER 判断给定元素是否在集合中 Set Is Member
SRANDMEMBER 随机返回集合中一个元素 Set Rand Member
SPOP 随机返回集合中一个元素,并且删除这个元素 Set Pop
SREM 删除集合中指定元素 Set Remove

6.有序集合对象

有序集合对象采用ziplist或者skiplist编码方式。其底层数据结构编码方式原则:

  • 有序集合保存的元素数量小于128个,有序集合对象采用ziplist编码方式
  • 有序集合对象报读的所有元素成员的长度都小于64字节,采用ziplist编码方式
  • 不满足上述两个条件时,采用skiplist编码方式。

如图是两种编码方式内存结构: 有序集合对象常用命令如下:

命令 功能 说明
ZADD 将新元素加入到有序集合  
ZCARD 返回有序集合中元素个数  
ZCOUNT 遍历有序集合统计分值在给定范围内的节点的数量  
ZRANGE 正序返回给定索引范围内的所有元素  
ZREVRANGE 逆序返回给定索引范围内的所有元素 Z Reverse Range
ZRANK 正序遍历集合,返回对应元素在集合中的排名  
ZREVRANK 逆序遍历集合,返回对应元素在集合中的排名 Z Reverse Rank
ZREM 删除给定的元素 ZRemove
ZSCORE 查找给定元素的分值 Z Score

总结

本文主要介绍Redis数据库中五大对象底层实现的原理,以及每种对象其底层源码编码方式的使用场景,并简单介绍各个对象常用的命令以及含义。

参考文献 《Redis设计与实现(第二版)》