Redis 底层数据结构

Redis 的高性能并非来自单一结构,而是通过多种针对场景高度定制的数据结构实现。上层五种数据类型(String / List / Hash / Set / ZSet)只是抽象接口,真正的性能边界由底层结构决定。

SDS(Simple Dynamic String)

基本原理

SDS 是 Redis 自定义的字符串实现,替代 C 原生 char*。其核心是显式维护字符串长度与剩余空间

典型结构:

1
2
3
4
5
struct sdshdr {
int len; // 已用长度
int alloc; // 分配总长度
char buf[];
}

核心特点

  • O(1) 获取长度(避免 strlen
  • 支持二进制数据(不依赖 \0作为字符串的结尾)
  • 预分配与惰性释放,减少内存重分配
  • 避免缓冲区溢出

使用到的数据类型

  • String
  • 所有 key
  • Hash / List / Set / ZSet 内部元素

时间复杂度

  • 获取长度:O(1)
  • 追加:均摊 O(1)
  • 修改:O(n)

链表(Linked List)

基本原理

双向链表,每个节点独立分配内存,保存前后指针与值指针。

核心特点

  • 双向
  • 无环
  • 表头表尾指针
  • 节点值为 void*,类型无关

使用到的数据类型

  • List(早期实现)
  • 发布订阅消息队列
  • 慢查询日志
  • 客户端列表

时间复杂度

  • 头尾插入 / 删除:O(1)
  • 按值查找:O(n)

现状

已被 quicklist 取代作为 List 的主要实现

压缩列表(ziplist)

基本原理

一段连续内存,通过偏移量描述节点,无指针。

每个 entry 包含:

  • 前一个 entry 的长度
  • 当前数据编码与长度
  • 实际数据

核心特点

  • 极致节省内存
  • 顺序存储,CPU cache 友好
  • 插入可能引发连锁更新

使用到的数据类型

  • List(旧)
  • Hash(小规模)
  • ZSet(小规模)

时间复杂度

  • 访问:O(n)
  • 插入 / 删除:O(n)

现状

Redis 7 之前使用,已被 listpack 全面替代

哈希表(Dict)

基本原理

标准 Hash Table + 链地址法,支持渐进式 rehash

核心结构:

  • 两张哈希表(ht[0], ht[1])
  • rehash index

核心特点

  • 扩容期间分批迁移
  • 避免阻塞
  • 支持自定义 hash 函数

使用到的数据类型

  • Hash(大规模)
  • Set
  • ZSet(score 映射)
  • 全局 key 空间

时间复杂度

  • 查找 / 插入 / 删除:O(1) 平均,O(n) 最坏
  • rehash:均摊 O(1)

整数集合(intset)

基本原理

用于存储仅包含整数的集合,底层为有序数组。

支持三种编码:

  • int16_t
  • int32_t
  • int64_t(自动升级,不降级)

核心特点

  • 内存占用极小
  • 自动升级保证正确性
  • 有序存储,二分查找

使用到的数据类型

  • Set(所有元素为整数且数量较少)

时间复杂度

  • 查找:O(log n)
  • 插入 / 删除:O(n)

跳表(SkipList)

基本原理

多层链表结构,通过随机层级实现近似平衡。

核心特点

  • 实现简单
  • 范围查询高效
  • 与哈希表组合使用

使用到的数据类型

  • ZSet(按 score 排序)

ZSet 实际结构:

  • Hash:member → score
  • SkipList:按 score 排序

时间复杂度

  • 查找 / 插入 / 删除:O(log n)
  • 范围查询:O(log n + m)

quicklist

基本原理

链表 + ziplist/listpack 的混合结构。

  • 外层:双向链表
  • 内层:压缩列表(旧)或 listpack(新)

核心特点

  • 降低指针开销
  • 控制单块内存大小
  • 平衡插入性能与内存效率

使用到的数据类型

  • List(Redis 3.2+)

时间复杂度

  • 头尾操作:O(1)
  • 中间访问:O(n)

listpack

基本原理

ziplist 的重写版本,彻底解决连锁更新问题

核心改进:

  • 不再存储前一个 entry 的长度
  • 更紧凑的编码设计
  • 更明确的边界信息

核心特点

  • 更安全
  • 更易扩展
  • 更适合大规模元素

使用到的数据类型

  • List
  • Hash
  • ZSet

时间复杂度

  • 访问 / 插入 / 删除:O(n)
  • 实际性能优于 ziplist

总结

Redis 的设计逻辑明确:

  • 小数据量 → 连续内存 → 节省内存
  • 大数据量 → 复合结构 → 保证性能
  • 写放大可接受,读路径必须稳定

理解这些底层结构,才能在以下问题上做出正确判断:

  • key 设计
  • value 结构选择
  • 内存占用评估
  • 性能瓶颈定位

Redis 的性能不是魔法,而是工程取舍的结果。