Redis的底层数据结构

SDS

①获取字符串长度的时间复杂度为O(1) ②支持动态扩容 ③减少内存分配次数 ④二进制安全

当新增内容时 如果新字符串小于 1M , 则新空间为扩展后字符串长度的两倍 +1 ; 如果新字符串大于 1M , 则新空间为扩展后字符串长度 +1M+1 。 称为内存预分配。

IntSet

如果新增加的元素范围超过了当前编码范围,intset会自动升级编码方式。并将每个元素从后向前拷贝到正确的位置。元素是按照大小顺序存放,查找时使用二分查找进行查询。插入时为保持顺序,需要移动元素。

ziplist:

markdown 复制代码
-   所有键值对的键和值长度都 < 64 字节
-   键值对数量 < 512 个
-   配置参数:`hash-max-ziplist-entries`、`hash-max-ziplist-value`

entry

Dict(字典)

diff 复制代码
-   不满足 ziplist 条件时使用
-   使用链地址法解决冲突,插入时使用头插法,避免需要遍历到链表的尾部才能插入。
-   渐进式rehash策略

Dict扩容

loadFactor=used/size。 当负载因子loadFactor>=1,并且没有bgSave,bgReWrite等后台进程在运行时会进行扩容。

Dict收缩

当负载因子<0.1时,会进行收缩。

rehash

QuickList

SkipList

RedisObject

Redis的五种数据类型的编码方式

Redis 五种基本数据类型的底层实现

Redis 的五种基本数据类型(String、List、Hash、Set、Sorted Set)在不同的使用场景下会采用不同的底层数据结构实现,这种灵活的设计使得 Redis 能够在内存使用和性能之间取得良好平衡。

1. String(字符串)

底层实现:

  • int:当存储的是整数值且可以用 long 类型表示时
  • embstr:当字符串长度 ≤ 44 字节时(Redis 5.0+)
  • raw:当字符串长度 > 44 字节时
  • SDS (Simple Dynamic String):所有字符串类型的底层基础结构

SDS 结构特点:

c 复制代码
struct sdshdr {
    int len;     // 已使用的长度
    int free;    // 未使用的空间
    char buf[];  // 实际存储的字符数组
};

优点:O(1)时间复杂度获取长度、二进制安全、减少内存重分配次数

2. List(列表)

底层实现:

  • ziplist(压缩列表)

    • 所有元素长度 < 64 字节
    • 元素数量 < 512 个
    • 配置参数:list-max-ziplist-sizelist-compress-depth
  • linkedlist(双向链表)

    • 不满足 ziplist 条件时使用
    • 每个节点都是独立的字符串对象 在redis3.2之前使用zipList和linkedList,3.2后使用quickList
  • quicklist(快速列表,Redis 3.2+)

    • ziplist 和 linkedlist 的混合体
    • 默认实现方式,由多个 ziplist 节点组成的双向链表
    • 配置参数:list-max-ziplist-size(每个ziplist节点大小)

3. Hash(哈希表)

底层实现:

  • ziplist

    • 所有键值对的键和值长度都 < 64 字节
    • 键值对数量 < 512 个
    • 配置参数:hash-max-ziplist-entrieshash-max-ziplist-value
  • hashtable(字典)

    • 不满足 ziplist 条件时使用
    • 使用链地址法解决冲突
    • 渐进式rehash策略

字典结构:

c 复制代码
typedef struct dict {
    dictType *type;    // 类型特定函数
    void *privdata;    // 私有数据
    dictht ht[2];      // 哈希表数组(用于rehash)
    long rehashidx;    // rehash索引
} dict;

4. Set(集合)

底层实现:

  • intset(整数集合)
    • 所有元素都是整数
    • 元素数量 < 512 个
    • 配置参数:set-max-intset-entries
  • hashtable
    • 不满足 intset 条件时使用
    • 只有键没有值(值设为NULL)

intset 结构:

c 复制代码
typedef struct intset {
    uint32_t encoding;  // 编码方式(int16/int32/int64)
    uint32_t length;    // 元素数量
    int8_t contents[];  // 元素数组
} intset;

5. Sorted Set(有序集合)

底层实现:

  • ziplist
    • 元素数量 < 128 个
    • 所有成员长度 < 64 字节
    • 配置参数:zset-max-ziplist-entrieszset-max-ziplist-value
  • skiplist + hashtable
    • 不满足 ziplist 条件时使用
    • 跳跃表用于排序和范围操作
    • 哈希表用于O(1)复杂度的成员查找

跳跃表结构:

c 复制代码
typedef struct zskiplistNode {
    robj *obj;                       // 成员对象
    double score;                    // 分值
    struct zskiplistNode *backward;  // 后退指针
    struct zskiplistLevel {
        struct zskiplistNode *forward;  // 前进指针
        unsigned int span;              // 跨度
    } level[];                       // 层
} zskiplistNode;

底层数据结构转换机制

Redis 会根据配置参数自动在这些底层实现之间转换:

  1. 当元素数量或大小超过阈值时,从小内存结构转为大内存结构
  2. 转换是单向的,不会自动转回去
  3. 可以通过修改配置参数调整转换阈值

为什么采用多种底层实现?

  1. 内存效率:对小数据使用紧凑存储(如ziplist)
  2. 性能优化:对不同操作提供最优时间复杂度
  3. 灵活性:根据数据特征自动选择最佳结构
  4. 平衡性:在内存使用和CPU效率之间取得平衡

理解这些底层实现有助于合理配置Redis参数,优化内存使用和性能表现。

相关推荐
在未来等你4 小时前
SQL进阶之旅 Day 29:NoSQL结合使用策略
redis·sql·mongodb·elasticsearch·postgresql·nosql·hybrid-database
运维老司机7 小时前
Redis 安装实践:基于鲲鹏 ARM 架构 Ubuntu 环境
arm开发·redis·架构
不良手残7 小时前
Redisson + Lettuce 在 Spring Boot 中的最佳实践方案
java·spring boot·redis·后端
明月看潮生9 小时前
青少年编程与数学 01-011 系统软件简介 16 Redis数据库
数据库·redis·青少年编程·编程与数学
让我上个超影吧10 小时前
黑马点评【缓存】
java·redis·缓存
blammmp11 小时前
Redis: List类型
数据库·redis·缓存
苏格拉没有底_coder14 小时前
Redis+Kafka实现动态延时任务
数据库·redis·kafka
seventeennnnn16 小时前
Java面试实战:Spring Boot+微服务+AI的谢飞机闯关之路 | CSDN博客精选
spring boot·redis·spring cloud·微服务·ai·java面试·rag
Zfox_16 小时前
Redis:渐进式遍历
服务器·数据库·redis·缓存
rufeike1 天前
Redis学习笔记
redis·笔记·学习