【基础篇】数据结构

Redis 的底层数据结构

我们都知道 Redis 中的数据结构有:String,List,Hash,Set,Sort Set等,那么他们对应的底层的数据结构有哪些呢?

可以看到,String 类型的底层实现只有一种数据结构,即简单动态字符串。但是,其他四种数据结构,都有两种底层实现结构,它们被称为"集合类型"。

上面的数据结构都是值的底层实现,键和值之间用什么结构组织呢?

键与值之间的关联

为了实现从键到值的快速访问,Redis 使用了一个哈希表来保存所有键值对。哈希表中每个元素称为一个哈希桶,每个哈希桶中保存了键值对数据。哈希桶中的元素保存的并不是值本身,而是指向具体值的指针。

哈希表的最大好处很明显,就是让我们可以用 O(1) 的时间复杂度来快速查找键值对。

但是,我们往 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。这其实是因为忽略了一个潜在的风险点,那就是哈希表的冲突问题和 rehash 可能带来的操作阻塞。

哈希表的哈希冲突

这里的哈希冲突,就是值两个 key 的哈希值和哈希桶计算对应关系的时候,正好落在同一个哈希桶里面。这一点很好理解,因为哈希桶的个数通常要少于 key 的数量。或者,你可以想象下:你有10 个篮子,12 个苹果,很明显,要全部放到篮子里的话,其中有两个篮子会有两个苹果。

Redis 中解决哈希冲突的方法,就是链式哈希。也即:同一个哈希桶中的多个元素,使用一个链表来保存:

这里回到我们之前说的点:往 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。很明显,当数据过多的时候,一个哈希桶上的哈希链就会越长,那么查询耗时就会增大,效率降低。这对与我们来说是不能接受的,所以我们需要对哈希表做 rehash 操作。

rehash 就是增加现有的哈希桶数量,让逐渐增多的 entry 元素能在更多的桶之间分散保存,减少单个桶中元素数量。具体操作如下:

  1. 为了 rehash 更有效率,Redis 默认使用两个全局哈希表:哈希表1 和哈希表2,一开始都使用 哈希表1;
  2. 当数据增多,要开始执行 rehash 时:
    • 给哈希表 2 分配更大的空间,比如说哈希表1 的两倍;
    • 把哈希表 1 中的数据重新映射到哈希表 2;
    • 释放哈希表 1 的空间;
  3. 哈希表 1就留着用于下一次 rehash 使用;
渐进式 rehash

上面的过程看似简单,但是数据的重新映射设计大量的数据拷贝,如果一次性将其迁移完成,会造成 Redis 线程阻塞。为了避免这个问题,Redis 采用了渐进式 rehash。

简单来说就是在第二步拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。

这样就巧妙地将一次性大量拷贝的开销,分摊到多次处理请求的过程中,避免了耗时操作。

集合类型的查找

对于 Sting 类型来说,找到哈希桶就能直接增删改查了,所以,哈希表的 O(1) 操作复杂度也就是它的复杂度了。

但是,对于集合类型来说,即使找到哈希桶了,还要在集合中再进一步操作。接下来,我们来看集合类型的操作效率又是怎样的。也即:第一步是通过全局哈希表找到对应的哈希桶位置,第二步是在集合中再增删改查。

集合类型的底层包括 5 种:整数数组、双向链表、哈希表、压缩列表和跳表。

一些使用上的点

  1. 范围操作,是指集合类型中的遍历操作,可以返回集合中的所有数据,比如 Hash 类型的 HGETALL 和 Set 类型的 SMEMBERS,或者返回一个范围内的部分数据。这类操作的复杂度一般是 O(N),比较耗时,我们应该尽量避免。
  2. 整数数组和压缩列表在查找时间复杂度方面并没有很大的优势,那为什么 Redis 还会把它们作为底层数据结构呢?
  • 内存利用率,数组和压缩列表都是非常紧凑的数据结构,它比链表占用的内存要更少。Redis是内存数据库,大量数据存到内存中,此时需要做尽可能的优化,提高内存的利用率。
  • 数组对CPU高速缓存支持更友好,所以Redis在设计时,集合数据元素较少情况下,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存不会降低访问速度。当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。(这点和Hash 类似: Hash 集合中写入的元素个数超过了 hash-max-ziplist-entries,或者写入的单个元素大小超过了 hash-max-ziplist-value,Redis 就会自动把 Hash 类型的实现结构由压缩列表转为哈希表。 还有 String 的 SDS 底层也是类型)
相关推荐
chenyuhao20241 小时前
链表的面试题4之合并有序链表
数据结构·链表·面试·c#
水水沝淼㵘2 小时前
嵌入式开发学习日志(数据结构--顺序结构单链表)Day19
linux·服务器·c语言·数据结构·学习·算法·排序算法
莹莹学编程—成长记3 小时前
list基础用法
数据结构·list
清幽竹客3 小时前
redis数据结构-09 (ZADD、ZRANGE、ZRANK)
数据结构·数据库·redis
葵花日记3 小时前
数据结构——二叉树
c语言·数据结构
越城4 小时前
数据结构中的栈与队列:原理、实现与应用
c语言·数据结构·算法
似水এ᭄往昔4 小时前
【数据结构】——栈和队列OJ
c语言·数据结构·c++
双叶8365 小时前
(C语言)超市管理系统(测试版)(指针)(数据结构)(二进制文件读写)
c语言·开发语言·数据结构·c++
想睡hhh7 小时前
c++进阶——哈希表的实现
开发语言·数据结构·c++·散列表·哈希
打鱼又晒网8 小时前
数据类型:List
数据结构·list