redis—底层数据结构详解

一、redisObject

在redis中基本的结构对象我们称之为RedisObject,其源码如下:

typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS;
int refcount;
void *ptr;
} robj;

其中:

  • type:表示值的数据类型。
  • encoding:值的编码方式,就是其底层的数据结构实现。
  • lru:记录了对象最后一次被访问的时间,用于淘汰过期的键值对。
  • refcount:记录了对象的引用计数。
  • *ptr:指向数据的指针

二、String的动态字符串

在redis底层中对,string的表示主要有三种结构,分别是int编码格式;embstr编码格式;raw编码格式。

  • 当长度小于20,并且是数值类型时,其底层就是用long来进行数据存储;这样做的好处就是可以减少空间占用,使用long的话最多也就占8个字节,使用char类型的话,会占用更多的内存空间。
  • 当存储是字符串的数据时,并且字符串的长度小于等于44字节时,以及长度大于22时,RedisObject和SDS会被分配到同一块内存空间,这样可以避免内存碎片化的问题,这种方式称为embstr编码方式。
  • 当存储是字符串的数据时,并且字符串的长度大于44字节时,那么RedisObject和SDS不会被分配在同一片内存空间,这种方式称为raw编码方式

其中SDS即Simple Dynamic String,其构造如下图所示:

其中:

  • len:占用4个字节,表示buf的已用长度
  • alloc:占用4个字节,表示buf实际分配的长度
  • buf:字节数组,保存实际的数据,为了表示字节数组的结束,redis默认在最后加一个"\0"多占用1个字节。

三、List

List数据结构:其底层的数据结构实现是 双向链表 + 压缩链表(ziplist),通过将每个压缩表用双向链表的方式连接起来,来节省内存空间。

ziplist是一个经过特殊编码的双向链表,它的设计目标就是为了提高存储效率,节省内存空间。ziplist可以用于存储字符串或整数,其中整数是按真正的二进制表示进行编码的,而不是编码成字符串序列。它能以O(1)的时间复杂度在表的两端提供push和pop操作。

其中:

  • zlbytes:表示列表长度
  • zltail:表示列表尾的偏移量
  • zllen:表示列表中entry的数量
  • zlend:用255来表示结束符

每个entry的结构如下:

  • prev_len:表示前一个entry的长度;有两种表示方式
    • 1字节——取1字节表示前一个entry的长度小于254个字节
    •  5字节——其它长度时,取5字节
  • encoding:表示编码方式,占用1字节
  • len:表示自身长度,占4字节
  • content:保存的实际数据

四、hash

Hash:其底层结构主要有两种方式来实现:hash表(dict)和ziplist,其默认实现是ziplist。

hash结构如下图所示:

并且在redis是存在两个hash表(hash1、hash2),一开始数据量不大的时候,我们的数据都会存放在hash1表中,当我们的key的数量在不断增加的时候,触发我们的rehash过程,这个时候hash2的结构是hash1的2倍,那么这个rehash的过程其实就是每一次操作过程中进行的,每次一个请求,都会讲一个哈希桶的数据放在hash2中,这样可以将操作分散在每个操作中,不会出现系统突然的卡顿现象。其流程如下:

rehash的过程:

  1. 给哈希表2分配更大的空间,例如是原来哈希表1的两倍
  2. 将哈希表1中的数据重新映射并拷贝到哈希表2中;
  3. 释放掉哈希表1中的空间

当发生如下两个情况时ziplist会转化成dict;并且这个过程是不可逆的。

  • 超过ziplist单个entry的长度限制;默认为64bytes
  • 超过ziplist最大entry个数限制:默认是512

五、Set

Set数据结构:其底层主要有整数数组(INTSET)和哈希表两种实现方式。当我们创建set的时候如果遇上成员是整形字符串时,会直接使用intset编码存储。intset的数据结构:

其中:整数数组的一个有序集合,其内部是采用二分查找来定位元素位置。

  • encoding表示编码格式:表示intset中每个元素采用以下哪种方式存储,有三种类型INT16(2字节),INT32(4个字节),INT64(8个字节)
  • length:元素个数,表示intset保存在contents中的元素个数
  • contents:实际存储的数据

每个编码格式的转化如下图所示:

 六、Sorted Set

zset数据结构:即有序的set,其底层时间是ziplist和skiplist。

skiplist结构如下图所示:

其中:skiplist的头部元素是不存储数据的,其值是存储在后面的节点中,span表示的就是步长

  • header表示头部元素,tail表示尾部元素
  • length表示元素个数
  • level表示层级

当符合以下条件中的任意一个时使用skiplist结构存储:

  • 配置了ziplist的最大元素个数为0,即不使用ziplist
  • 配置了ziplist的最大元素个数,并且zset元素大于它

其中zset底层两种结构可以发生相互转化的过程

  • ziplist转化成skiplist
    • 当ziplist的元素个数大于配置的最大值时,会强制转化成skiplist编码格式(默认128)
    • 当要插入ziplist的元素长度大于配置的最大值时,会强制转化成skiplist编码格式(默认64)
  • skiplist转换成ziplist

    • skiplist中元素长度小于配置的ziplist最大值,并且skiplist中元素的最大长度小于等于配置的ziplist元素最大值

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值