Redis 数据结构解析

在这里插入图片描述


一、Redis 数据结构的核心设计

Redis 的 数据类型(对象)底层数据结构 是解耦的,同一数据类型在不同场景下可能采用不同的底层结构。例如,List 类型在元素较少时使用压缩列表(ZipList),元素较多时则切换为双向链表或 QuickList。


二、核心数据结构与底层实现

数据类型底层数据结构关键特性
String- SDS(simple dynamic string, 简单动态字符串)
- 整数(直接存储数值)
- SDS 支持预分配和惰性释放,避免频繁内存操作
- 最大长度 512MB,二进制安全(可存储图片等二进制数据)
List- ZipList(压缩列表)(元素少时)
- LinkedList(双向链表)(Redis 3.0前)
- QuickList(3.2+,链表+ZipList组合)
- ListPack(最新版替代 ZipList)
- QuickList 结合链表和ZipList优势,减少内存碎片
- ListPack 解决 ZipList 级联更新问题,提升性能
Hash- ZipList(字段少且值小时)
- HashTable(哈希表)(默认)
- 哈希表采用链地址法解决冲突,渐进式 Rehash 避免阻塞
- 压缩列表存储时字段和值连续排列,节省内存
Set- IntSet(整数集合)(全为整数时)
- HashTable(其他情况)
- IntSet 自动升级存储类型(int16 → int32 → int64)
- 哈希表值固定为 NULL,仅用键存储元素
ZSet- ZipList(元素少时)
- SkipList(跳表) + HashTable(元素多时)
- 跳表支持 O(logN) 范围查询,哈希表实现 O(1) 单元素访问
- 元素按分值排序,支持排行榜等场景

三、底层数据结构详解

  1. SDS(简单动态字符串)

    • 结构len(长度)、free(剩余空间)、buf[](数据)
    • 优势
      • 常数复杂度获取长度(strlen 直接读 len)。

      • 避免缓冲区溢出(自动扩容)。

      • 支持二进制安全(允许 \0 作为内容)。

        上述优势都是 c 语言 字符数组不具备的。

  2. ZipList(压缩列表)

    • 应用场景:List、Hash、ZSet 的小数据存储。
    • 结构:连续内存块,元素由 prev_len(前驱节点长度)、encoding(编码类型)、content(数据)组成。
    • 缺点:级联更新问题(某一节点扩展导致后续节点 prev_len 连锁更新)。
  3. QuickList

    • 改进ZipList:降低级联更新影响,将一整个ZipList(压缩列表)拆分为多个ZipList;尽可能缩小级联更新范围;
  4. ListPack

    • 改进ZipList:ZipList级联更新罪魁祸首就是prev_len(前驱节点长度);所以 ListPack 去掉了这个前面节点长度的字段,改为记录当前节点的长度,当我们向 ListPack 加入一个新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题。
  5. SkipList(跳表)

    • 核心逻辑:多层索引加速查找,节点随机层高(基于幂次定律)。
    • 优势:支持高效范围查询(如 ZRANGE),复杂度 O(logN)。
    • 结构:每个节点包含 score(分值)、backward(后退指针)、level[](层级数组)。
  6. HashTable(哈希表)

    • 渐进式 Rehash:扩容时同时操作新旧哈希表,避免单次 Rehash 阻塞服务。
    • 冲突解决:链地址法(头插法),哈希函数为 SipHash。

四、版本演进与优化

  • Redis 3.0 → 3.2:List 从 ZipList/LinkedList 改为 QuickList(减少内存碎片)。
  • Redis 5.0+:废弃 ZipList,引入 ListPack(解决级联更新问题)。
  • Redis 7.0:默认使用 ListPack 作为 Hash 和 ZSet 的底层结构。

五、性能优化建议

  1. 控制数据规模
    • Hash/Set/ZSet 元素较少时优先使用压缩结构(ZipList/ListPack),减少内存占用。
  2. 避免大 Key
    • String 类型 Value 不超过 10KB,Hash/List 元素数量控制在 1000 以内。
  3. 合理选择数据类型
    • 频繁范围查询 → ZSet(SkipList)
    • 高性能计数 → String(INCR/DECR)
    • 去重存储 → Set(IntSet/HashTable)

六、高频面试题

  1. 为什么 ZSet 同时使用跳表和哈希表?
    • 跳表支持范围查询,哈希表实现 O(1) 单元素访问,两者互补。
  2. Redis 如何解决哈希冲突?
    • 链地址法,哈希桶 + 链表结构,新元素插入链表头部。
  3. ListPack 如何优化 ZipList 的缺陷?
    • 取消 prev_len 字段,通过固定长度编码避免级联更新。

扩展阅读

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值