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参数,优化内存使用和性能表现。

相关推荐
不想当程序猿_24 分钟前
Centos 7系统 宝塔部署Tomcat项目(保姆级教程)
linux·redis·centos·tomcat·minio·宝塔
左灯右行的爱情41 分钟前
Redis 缓存并发问题深度解析:击穿、雪崩与穿透防治指南
java·数据库·redis·后端·缓存
我学上瘾了3 小时前
链表反转_leedcodeP206
网络·redis·链表
Seven974 小时前
缓存穿透的解决方式?—布隆过滤器
java·数据结构·redis
Themberfue9 小时前
Redis ⑦-set | Zset
java·开发语言·数据库·redis·sql·缓存
青铜爱码士17 小时前
redis+lua+固定窗口实现分布式限流
redis·分布式·lua
王景程18 小时前
如何使用 Redis 缓存验证码
redis·缓存·mybatis
编程在手天下我有18 小时前
Redis 常见问题深度剖析与全方位解决方案指南
数据库·redis·缓存·性能优化·数据持久化·分布式系统
bxlj_jcj1 天前
如何实现Redis和Mysql中数据双写一致性
redis·缓存·架构