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

相关推荐
摇滚侠8 小时前
阿里云安装的 Redis 在什么位置,如何找到 Redis 的安装位置
redis·阿里云·云计算
啦啦啦_99999 小时前
Redis-2-queryFormat()方法
数据库·redis·缓存
forestsea11 小时前
深入理解Redisson RLocalCachedMap:本地缓存过期策略全解析
redis·缓存·redisson
佛祖让我来巡山11 小时前
Redis 为什么这么快?——「极速快递站」的故事
redis·redis为什么快?
啦啦啦_999913 小时前
Redis-0-业务逻辑
数据库·redis·缓存
自不量力的A同学14 小时前
Redisson 4.2.0 发布,官方推荐的 Redis 客户端
数据库·redis·缓存
fengxin_rou14 小时前
[Redis从零到精通|第四篇]:缓存穿透、雪崩、击穿
java·redis·缓存·mybatis·idea·多线程
是阿楷啊15 小时前
Java大厂面试场景:音视频场景中的Spring Boot与微服务实战
spring boot·redis·spring cloud·微服务·grafana·prometheus·java面试
笨蛋不要掉眼泪15 小时前
Redis哨兵机制全解析:原理、配置与实战故障转移演示
java·数据库·redis·缓存·bootstrap
ALex_zry1 天前
Redis Cluster 分布式缓存架构设计与实践
redis·分布式·缓存