Redis 数据结构底层实现 – wiki基地

Redis 数据结构底层实现深度解析

Redis 作为一个高性能的键值存储数据库,其卓越的性能很大程度上归功于其精妙的数据结构设计。Redis 不仅仅是简单的键值对存储,它支持多种丰富的数据结构,包括字符串(String)、列表(List)、哈希表(Hash)、集合(Set)和有序集合(Sorted Set)。理解这些数据结构的底层实现,对于深入掌握 Redis 的性能特性以及高效地运用 Redis 至关重要。

1. 字符串(String)

Redis 的字符串对象是使用名为 SDS (Simple Dynamic String) 的自定义结构实现的,而不是简单的 C 字符串。SDS 的结构定义如下:

c
struct sdshdr {
// 记录 buf 数组中已使用字节的数量,等于 SDS 所保存字符串的长度
int len;
// 记录 buf 数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串
char buf[];
};

SDS 相比于 C 字符串的优势在于:

  • 常数时间复杂度获取字符串长度: len 属性直接保存了字符串长度,避免了 C 字符串 strlen 函数的 O(n) 时间复杂度。
  • 避免缓冲区溢出: SDS 通过 free 属性记录剩余空间,在进行字符串修改操作时,会先检查空间是否足够,如果不足则会自动扩展,避免了 C 字符串容易出现的缓冲区溢出问题。
  • 减少内存重新分配次数: SDS 采用空间预分配策略,当字符串修改后,SDS 会预分配一部分空闲空间。如果修改后的字符串长度小于 1MB,则预分配和 len 属性相同大小的空闲空间;如果大于 1MB,则预分配 1MB 的空闲空间。这样可以减少内存重新分配的次数,提高性能。
  • 惰性空间释放: 当字符串缩短时,SDS 不会立即释放多余的内存空间,而是通过 free 属性记录下来,以备后续使用。
  • 二进制安全: SDS 的 buf 数组可以保存任意二进制数据,不仅仅是文本数据,因为 SDS 通过 len 属性来判断字符串的长度,而不是依赖于空字符 \0

2. 列表(List)

Redis 的列表底层实现使用了两种数据结构:双端链表压缩列表(ziplist)

  • 双端链表: 当列表元素较多或元素长度较大时,Redis 使用双端链表来存储列表元素。每个链表节点包含指向前一个节点和后一个节点的指针,以及存储元素值的指针。双端链表的优势在于插入和删除元素的效率很高,时间复杂度为 O(1)。
  • 压缩列表: 当列表元素较少且元素长度较小时,Redis 使用压缩列表来节省内存。压缩列表是一块连续的内存空间,依次存储列表元素,每个元素的长度和值都紧密地排列在一起。压缩列表的优点是内存占用小,但缺点是插入和删除元素的效率较低,需要进行内存拷贝操作,时间复杂度为 O(n)。

Redis 会根据列表元素的数量和大小动态选择使用双端链表还是压缩列表。

3. 哈希表(Hash)

Redis 的哈希表底层使用了两种数据结构:压缩列表字典(dict)

  • 压缩列表: 与列表类似,当哈希表中的键值对数量较少且键值对的长度较小时,Redis 使用压缩列表来存储。
  • 字典: 当哈希表中的键值对数量较多或键值对的长度较大时,Redis 使用字典来存储。字典的底层实现是哈希表,使用链地址法解决哈希冲突。Redis 的字典包含两个哈希表,用于实现渐进式 rehash,以避免一次性 rehash 造成的性能抖动。

4. 集合(Set)

Redis 的集合底层使用了两种数据结构:整数集合(intset)字典

  • 整数集合: 当集合中的所有元素都是整数且元素数量较少时,Redis 使用整数集合来存储。整数集合是一个有序的整数数组,可以有效地节省内存。
  • 字典: 当集合中的元素不是全部为整数或元素数量较多时,Redis 使用字典来存储。字典的键存储集合元素,值则是一个空值。

5. 有序集合(Sorted Set)

Redis 的有序集合底层使用了两种数据结构:压缩列表跳跃表(skiplist) 配合字典。

  • 压缩列表: 当有序集合中的元素数量较少且元素长度较小时,Redis 使用压缩列表来存储。
  • 跳跃表和字典: 当有序集合中的元素数量较多或元素长度较大时,Redis 使用跳跃表和字典来存储。跳跃表是一种有序的数据结构,可以高效地进行范围查询和排序操作。字典用于存储元素和分数的映射关系。跳跃表中的节点包含元素成员和对应的分数,通过分数来排序。

总结:

Redis 为了兼顾内存使用效率和性能,针对不同的数据结构和使用场景,采用了多种底层数据结构,并通过一定的策略进行动态切换。理解这些底层实现,可以帮助我们更好地选择合适的数据结构,并优化 Redis 的使用,从而最大程度地发挥 Redis 的性能优势。 深入了解这些细节,不仅能帮助我们更好地使用 Redis,还能提升我们对数据结构和算法的理解,在更广泛的领域中发挥作用。 通过对不同场景下数据结构的选择和优化,我们可以构建更高效、更稳定的应用程序。 未来,Redis 可能会引入更多优化策略和新的数据结构,以适应不断变化的需求和技术发展。 持续学习和关注 Redis 的发展,对于保持技术竞争力至关重要。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部