【基础篇】数据结构

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 底层也是类型)
相关推荐
闪电悠米3 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
WL学习笔记4 小时前
堆(数据结构)
数据结构·
wabs6668 小时前
关于图论【卡码网100.最大岛屿的面积的思考】
数据结构·算法·图论
yuannl109 小时前
图的最短路径Dijkstra
数据结构
zander25810 小时前
138. 随机链表的复制
数据结构·算法·链表
过期动态11 小时前
【LeetCode 热题 100】找到字符串中所有字母异位词
java·数据结构·算法·leetcode·职场和发展·rabbitmq
来一碗刘肉面11 小时前
栈在递归中的应用
数据结构·算法
皓月斯语11 小时前
程序设计语言的特点
开发语言·数据结构·c++
邪修king12 小时前
C++ 进阶终章:异常机制与智能指针全解 —— 从错误处理到 RAII 资源管理,打通现代 C++ 的核心命脉
android·数据结构·c++
Adios79419 小时前
设置交集大小至少为2
数据结构·算法·leetcode