redis数据结构

基本数据结构与底层数据结构

image

 SDS(Simple Dynamic String) - 简单动态字符串

struct sdshdr {
    int len;    // 记录buf数组中已使用字节的数量,即字符串长度(获取长度O(1))
    int free;   // 记录buf数组中未使用字节的数量
    char buf[]; // 字节数组,用于保存字符串,遵循C语言以'\0'结尾的惯例
};

  SDS 在修改前会先检查空间是否足够,不够则先扩容。

压缩列表 (ZipList)

  ziplist 是 Redis 提供的一种“紧凑型列表结构(compact list)”。ZipList(压缩列表)不是链表,而是一种连续内存块组成的顺序型数据结构。它将多个 list 元素连续地存放在一块 连续内存(contiguous memory block) 中。

image

Zlbytes:压缩列表的字节长度 
Zltail:压缩列表尾元素相对于起始地址的偏移量
Zllen:压缩列表的元素数目
entry:包含prevlen(前一个元素的长度)、encoding(data数据类型)、data.

用途:列表键(List)和哈希键(Hash)的底层实现之一(当元素较少且值较小时)。在 Redis 7.0 之后,被 ListPack 全面替代。
设计理念:为了节省内存,压缩列表是一段连续内存块组成的顺序型数据结构。它通过记录每个条目的长度和编码信息,紧凑地排列数据。。

连锁更新问题:由于每个节点都记录了前一个节点的长度(通过 prevlen 字段)。如果修改了某一个节点的长度(比如从 253 字节变为 254 字节),可能会导致后续节点的 prevlen 存储空间从 1 字节膨胀到 5 字节,进而引发后续节点的连锁更新,性能下降。这是 ZipList 的一个缺陷,也是被 ListPack 淘汰的原因
View Code

紧凑列表 (ListPack)

用途:作为 ZipList 的替代品【Redis 7.0 后】,是列表和哈希的默认底层实现之一。

  • 改进点:ListPack 解决了 ZipList 的连锁更新问题。它不再存储前一个节点的长度,而是存储当前节点自身的长度,从而避免了修改一个节点影响后续节点的情况。设计依然保持内存紧凑。

字典(Dict)

在 Redis 中,字典无处不在:

  • 整个 Redis 数据库的所有键值对都存储在一个字典中(redisDb 的 dict 属性)
  • 哈希键(Hash)当元素较多时使用字典实现
  • 集合键(Set)当元素为非整数或数量多时使用字典实现(键为元素,值为 NULL)
  • 每个 Redis 对象都有一个 type 属性和 ptr 指针,这些信息也通过字典管理
typedef struct dictEntry {
    void *key;              // 键(Set 的元素)
    union {
        void *val;          // 值(Set 不用)
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;
View Code

快速列表 (QuickList)

 

image

 用途:列表键(List)的底层实现(3.2 版本之后)

  • 本质:QuickList 是一个双向链表,但它的每个节点不是一个简单的值,而是一个 ZipList 或 ListPack。

  • 设计哲学:结合了双向链表(方便在两端快速插入删除)和压缩列表(内存紧凑)的优点。既保证了性能,又控制了内存碎片

 

兼顾空间与效率:通过将列表拆成多个小段(ziplist),既避免了大型链表带来的内存碎片和高 overhead,也避免了大型 ziplist 带来的频繁 realloc 问题。
快速头尾操作(push/pop):因为 quicklist 是链表结构,在头尾插入删除是 O(1) 操作,非常高效,适合队列/栈等操作频繁场景。
压缩可选:中间节点可以压缩(LZF 等算法/listpack 压缩),节省内存;而头尾保留原始以保证访问性能。
灵活适应不同场景:既适合存储小量短元素,也适合存储大量元素或长列表。

跳表 (Skip List)

用途:有序集合键(Sorted Set)的底层实现之一。

  • 为什么不用平衡树?

    • 跳表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

    • 优点:平均查找性能 O(logN),实现比平衡树简单,且范围查找(如 ZRANGE)非常方便。Redis 的作者认为跳表在代码实现和调试难度上远低于平衡树。

  • 结构:由多层链表组成,上层是下层的“快速通道”,允许跳过部分节点进行搜索。

 

level 3:  head ---------------------------------------------> tail
          |                                                   
level 2:  head -----------> A -----------> C -----------> tail
          |                |                |               
level 1:  head --> A --> B --> C --> D --> E --> F --> tail
          |      |      |      |      |      |      |      
数据层:    [null]  [A]    [B]    [C]    [D]    [E]    [F]   
score:           1.0    2.0    3.0    4.0    5.0    6.0
  • 最底层(level 1):包含所有节点,是有序链表

  • 上层(level 2, 3):是下层的"快速通道",跳过部分节点

 

posted @ 2026-03-20 12:24  鄙人取个名字好难  阅读(1)  评论(0)    收藏  举报