认识Redis高性能背后的数据结构(二)

前言

前面一篇文章我们已经了解了String、List、Hash的底层数据结构实现,那接下来我们就一起看一下Set 和ZSet的底层数据结构的实现。废话不多说,直接开搞。

1.集合set

1.1 set 的底层实现

还是通过Redis的客户端来看一下set的底层数据结构:

上图是我直接用Redis的客户端直接添加数据,这里我添加的value类型全都是数字,当我们key对应的集合数量较小的时候,底层数据结构是intset,当key所对应的集合数量超过了某一个值,底层数据结构变成了hashtable,我们之前也说了hashtable是字典的底层实现,所以我们用字典来代替hashtable。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6ZvE78fv-1596962075459)(https://s1.ax1x.com/2020/08/02/athkGR.png)]

上面这个图,我给key对应的value值只有10个,底层数据结构仍然是intset,但是当我再增加一个字符串,底层的数据结构也从intset转成了dict。

除此之外呢,我们可以通过数据结构的名称能够才想到,intset只能存储int类型的数字,intset能够表达的最大的整数范围为-2^64 ~ 2^64-1,如果添加的数字大小超出了这个范围,这也会导致intset转成dict。所以我们可以总结如下:

  1. 当集合中的key 对应的 value为一个数字(key对应的value只能是数字),且当数字范围在-264~264-1中间,且添加的集合元素个数没有超过set-max-intset-entries配置的值的时,集合键Set底层数据结构为intset。
  2. 其他的情况底层数据结构为字典dict。

1.2 整数集合 Intset

我们知道了Redis的集合键Set的底层实现有两种数据结构:字典dict、整数集合intset。前面一篇的文章我们已经说过了字典dict的实现,这里我们就重点来说一下intset。

intset的数据结构定义如下(代码来自intset.h):

typedef struct intset {
   
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
} intset;

  • encoding:数据编码格式,表示intset中每个元素用几个字节来存储。

    1. INTSET_ENC_INT16表示每个元素用2个字节存储。
    2. INTSET_ENC_INT32表示每个元素用4个字节存储
    3. INTSET_ENC_INT64表示每个元素用8个字节存储

    所以intset中存储的整数最多只能占用64bit。

  • length: 表示intset中的元素个数。

  • contents:这是一个动态数组,是真正存储数据的。这个数组的总长度等于encoding * length。这里我们需要注意的是,intset的编码格式会随着新增加的元素调整,根据元素的大小是否对数据编码进行升级。

intset的内存是连续的,所有的数据增删改查操作都是在内存地址偏移的基础上进行的,并且整数的保存也是有序的。

1.2 为什么要设计整数集合intset

相比于字典dict来说,使用intset来存储,主要的原因是节省内存。特别是当存储key所对应的元素个数较少的时候,相对于dict来说,dict所需要的内存开销要大得多,因为字典包含两个哈希表、链表指针以及大量的其它元数据。所以,当存储大量的小集合而且集合元素都是数字的时候,用intset能节省下一笔可观的内存空间。

可能有朋友会提问了,就算dict使用的内存开销稍微大一些,但是dict的查询的时间复杂度是O(1),相比于intset来说平均查询时间要快的,对于追求高性能的Redis来说当然还是选用dict稍微好一些。这里我想要提醒大家一点,大家不要忘记了intset是一个排序好的集合,排序好的集合我们可以利用二分法来进行数据的查找,其时间复杂度为O(logn),由于使用intset的时集合元素个数比较少,所以这个影响不大。

2. 有序集合zset

2.1 zset的底层实现

有序集合在集合的基础上扩展出来了有序集合,zset实现的底层数据结构有两种:压缩列表ziplist和跳跃表zskiplist

当zset使用ziplist做底层实现时需要满足以下两个条件:

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

我们之前对ziplist的数据结构已经分析过了,这里我们就不重复说了,如果忘记了话,可以看上一篇文章。如果zset底层的数据结构使用的是ziplist,每个元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个节点保存元素的分值。并且按分值从小到大的顺序进行排列。如下图:

1594524497517.png

2.2 跳表 skiplist

2.2.1 什么是跳表

跳表全称为跳跃列表,它允许快速查询,插入和删除一个有序连续元素的数据链表。跳跃列表的平均查找和插入时间复杂度都是O(logn)。

跳表是在单链表的基础上构建的,我们知道单链表的数据结构优势在于插入和删除的时间复杂度都是O(1),但是单链表的查询效率是非常低的,时间复杂度为O(n),即使我们数据是有序的,

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值