Redis数据类型的底层实现原理

401 阅读5分钟

redis 数据类型和底层实现概况

Redis对外提供的数据类型

redis 提供的5种数据类型如下:

  1. string 类型
  2. list 类型
  3. set 类型
  4. hash类型
  5. zset 类型

Redis数据类型的底层数据结构

redis 底层提供的数据结构如下:

  1. dict 字典结构
  2. sds 简单动态字符串结构
  3. intset 整数集合结构
  4. linkedList 双端链表结构
  5. ziplist 压缩列表结构
  6. quicklist 快速列表结构
  7. skiplist 跳表结构

redis 底层数据结构和实现原理介绍

redis中 key 的存储

redisobject

// redisobject 至少占 16 byte , 再加上字符串的结尾标识符号 '\0',就是为什么embstr 存储不超过 44 字节
 typedef struct redisObject {
    unsigned type:4; // 占4 bits, 表示具体的数据类型
    unsigned encoding:4; // 占 4 bits , 表示具体的编码方式
    unsigned lru:LRU_BITS; // 占 24 bits,表示最近一次被访问的时间 
    int refcount ; // 占4 bytes , 表示对象被引用的计数值
    void * ptr; // 占 8 bytes , 表示执行数据地址的指针
 } robj;

redisDb

redis key 的存储过程

1、 计算key 的hash 值

2、 计算hash值在内存中的存储索引位置

[3、 解决hash 冲突]

4、对应字典dict(也叫hashtable)节点的下标,实质是dictEntry的下标

dict 结构详解

dict 字典结构介绍

dict 字典结构

sds 结构详解

redis 中的 string 类型底层的的3种编码形式

  • OBJ_ENCODING_INT (int)
  • OBJ_ENCODIG_EMBSTR (embstr)
  • OBJ_ENCODING_RAW (raw)

场景一:以int 形式编码存储

当 value 值为 long 类型的整数,且长度小于 20 字节,以int 存储。

场景二:以 raw 形式编码存储

当value值为字符串,且长度大于 44 字节,以raw 存储。

场景三:以 embstr 形式编码存储

当value 值为字符串,且长度小于 44 字节,以embstr 存储。

: embstr 形式的编码,内存上是连续的,而raw形式的编码,内存是不连续的。

string 类型 的值,底层是采用 sds 存储。

sds ,简单动态字符串,是redis 封装的的一种数据结构。C 语言 字符串的空字符处理不足, redis 重新定义了一个表示字符串的数据结构 sds 。如下是 sds 的数据结构定义 :

3.x 版本的定义 :

   struct sdshdr {
       unsigned int len; // 记录buf[] 中已经使用的字节数
       
       unsigned int free; // 记录buf[] 中为使用的字节数 , 思考是不是有点多月
       
       char buf [];
   }

6.x 版本的定义:

  struct sdshrd8 {
      uint8_t len; // 记录buf[] 已经使用的字节长度
      uint_t alloc; // buf[] 分配的总长度
      unsigned char flags; // 低3位保存数据类,高5位保留
      char buf[];
  } sdshdr8;
  
  // 类的的 还有sdshdr16、sdshdr32、sdshdr64、sdshdr5
  

redis 底层采用 sds 存储的优势

1、 O(1) 时间复杂度获取字符串的长度

2、数据的存储是二进制安全的

3、sds 拥有自动扩充机制,杜绝缓冲区溢出现象

4、sds 拥有惰性空间释放机制,减少内存重新分配的次数。

intset 结构详解

linkedList 结构详解

linkedlist 数据结构

linkedlist 双端链表结构,由2个指针节点 listNode 和一个链表长度组成。

双端链接数据结构 (list数据结构)如下:

typedef struct list{
     
     listNode *head ;
     listNode *tail;
     long len;
 } list ; 

linkedlist 中每个节点 listNode 的数据结构如下 :

typedef struct listNode {

    struct listNode *prev; // 前驱节点
    struct listNode *next; // 后继节点
    void *value; // 节点内容
 
} listNode ;

双端链表数据结构的优点

1、双端,linkedlist是双端链表,在表头和表尾操作元素;

2、无环,linkedlist链表的表头prev指针指向null,表尾next指针指向null,没有构成环;

3、获取链表长度的算法时间复杂度是常数级,len属性记录链表长度,获取长度的时间负责读是 O(1) ;

4、带表头指针和表尾指针;

5、多态,可以存储数字类型,也可以存储字符串类型,也可以存储对象类型 ;

双端链表数据结构的缺点

1、linkedlist 在内存中开辟的内存空间不是连续的,不能通过内存空间的方式直接定位到元素,元素的获取耗性能;

2、因为在内存中开辟的内存空间不是连续的,数据量很大的时候会产生很多内存碎片,内存有效利用率不高;redis 是基于内存的数据结构,内存的有效利用率要求很高。linkedlist 数据结构和redis的内存有效利用要求之间是有矛盾的。所以redis底层并不是直接采用c实现的双端链表,而是采用ziplist(压缩链表)代替了linkedlist 。

ziplist 结构详解

ziplist

ziplist(压缩列表)是redis为节省内存而设计的。是由一系列特殊编码的连续空间数组块组成的顺序型的数据结构。一个ziplist可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者多个整数值。

ziplist 数据结构

ziplist 数据结构

 typedef struct ziplist {
 
 } ziplist;

entry数据结构

typedef struct entry{

} entry ;

ziplist 数据结构的优点:

1、

2、

3、

ziplist 数据结构的缺点:

1、

2、

3、

4、redis 在 3.2版本开始引入了 quicklist 数据结构,联合ziplist 同时使用。

quicklist 结构详解

skiplist 结构详解

redis 的数据类型和底层的实现对应关系

string类型底层实现

list类型底层实现

set类型底层实现

hash类型底层实现

zset类型底层实现