Redis底层结构一

Redis底层结构,更多关注redisDb结构体和redisObject

RedisDB结构

Redis中存在“数据库”的概念,该结构由server.h中的redisDb定义。

当redis 服务器初始化时,会预先分配 16 个数据库

所有数据库保存到结构 redisServer 的一个成员 redisServer.db 数组中

redisClient中存在一个名叫db的指针指向当前使用的数据库

RedisDB结构体源码:

typedef struct redisDb {
  int id;                 //id是数据库序号,为0-15(默认Redis有16个数据库)
  long avg_ttl;     //存储的数据库对象的平均ttl(time to live),用于统计
  dict *dict;         //存储数据库所有的key-value
  dict *expires;     //存储key的过期时间
  dict *blocking_keys;//blpop 存储阻塞key和客户端对象
  dict *ready_keys;    //阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象 
  dict *watched_keys;//存储watch监控的的key和客户端对象
} redisDb;

id

id是数据库序号,为0-15(默认Redis有16个数据库)

dict

存储数据库所有的key-value

expires

存储key的过期时间

RedisObject结构

Value是一个对象

包含字符串对象,列表对象,哈希对象,集合对象和有序集合对象

结构信息概览

typedef struct redisObject { 
      unsigned type:4;//类型 对象类型 
    unsigned encoding:4;//编码
        void *ptr;//指向底层实现数据结构的指针 
     //...
        int refcount;//引用计数
        //...
        unsigned lru:LRU_BITS; //LRU_BITS为24bit 记录最后一次被命令程序访问的时间 
    //...
}robj;

4位type

type 字段表示对象的类型,占 4 位;

当我们执行 type 命令时,便是通过读取 RedisObject 的 type 字段获得对象的类型

127.0.0.1:6379> type k1
string

4位encoding

encoding 表示对象的内部编码,占 4 位 每个对象有不同的实现编码 Redis 可以根据不同的使用场景来为对象设置不同的编码,大大提高了 Redis 的灵活性和效率。

通过 object encoding 命令,可以查看对象采用的编码方式

127.0.0.1:6379> object encoding k1
"embstr"

24位LRU

lru 记录的是对象最后一次被命令程序访问的时间,( 4.0 版本占 24 位,2.6 版本占 22 位)。

高16位存储一个分钟数级别的时间戳,低8位存储访问计数(lfu : 最近访问次数)

lru----> 高16位: 最后被访问的时间

lfu----->低8位:最近访问次数

refcount

refcount 记录的是该对象被引用的次数,类型为整型。

refcount 的作用,主要在于对象的引用计数和内存回收。

当对象的refcount>1时,称为共享对象

Redis 为了节省内存,当有一些对象重复出现时,新的程序不会创建新的对象,而是仍然使用原来的对象。

ptr

ptr 指针指向具体的数据,比如:set hello world,ptr 指向包含字符串 world 的 SDS。

10种encoding

encoding 表示对象的内部编码,占 4 位。 Redis通过 encoding 属性为对象设置不同的编码

少的和小的数据,Redis采用小的和压缩的存储方式,体现Redis的灵活性,大大提高了 Redis 的存储量和执行效率 比如Set对象:

intset : 元素是64位以内的整数

hashtable:元素是64位以外的整数

如下所示:

127.0.0.1:6379> sadd set:001 1  3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"

String

int、raw、embstr

1. int

REDIS_ENCODING_INT(int类型的整数)

127.0.0.1:6379> set n1 123
OK
127.0.0.1:6379> object encoding n1
"int"

2. embstr

REDIS_ENCODING_EMBSTR(编码的简单动态字符串)

小字符串 长度小于44个字节

127.0.0.1:6379> set name:001 zhangfei
OK
127.0.0.1:6379> object encoding name:001
"embstr"

3. raw

REDIS_ENCODING_RAW (简单动态字符串)

大字符串 长度大于44个字节

127.0.0.1:6379> set address:001
asdasdasdasdasdasdsadasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdas
dasdasdas
OK
127.0.0.1:6379> object encoding address:001
"raw"

list

列表的编码是quicklist。

REDIS_ENCODING_QUICKLIST(快速列表)

127.0.0.1:6379> lpush list:001 1 2 5 4 3
(integer) 5
127.0.0.1:6379> object encoding list:001
"quicklist"

hash

散列的编码是字典和压缩列表

1. dict

REDIS_ENCODING_HT(字典)

当散列表元素的个数比较多或元素不是小整数或短字符串时。

127.0.0.1:6379>  hmset user:003
username111111111111111111111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111  zhangfei password 111 num
2300000000000000000000000000000000000000000000000000
OK
127.0.0.1:6379> object encoding user:003
"hashtable"

2. ziplist

REDIS_ENCODING_ZIPLIST(压缩列表)

当散列表元素的个数比较少,且元素都是小整数或短字符串时。

127.0.0.1:6379> hmset user:001  username zhangfei password 111 age 23 sex M
OK
127.0.0.1:6379> object encoding user:001
"ziplist"

set

集合的编码是整形集合和字典

1. intset

REDIS_ENCODING_INTSET(整数集合)

当Redis集合类型的元素都是整数并且都处在64位有符号整数范围内(<18446744073709551616)

 127.0.0.1:6379> sadd set:001 1  3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"

2. dict

REDIS_ENCODING_HT(字典)

当Redis集合类型的元素是非整数或都处在64位有符号整数范围外(>18446744073709551616)

127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"

zset

有序集合的编码是压缩列表和跳跃表+字典

1. ziplist

REDIS_ENCODING_ZIPLIST(压缩列表)

当元素的个数比较少,且元素都是小整数或短字符串时。

127.0.0.1:6379> zadd hit:1 100 item1 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:1
"listpack"

2. skiplist

REDIS_ENCODING_SKIPLIST(跳跃表)

当元素的个数比较多或元素不是小整数或短字符串时。

127.0.0.1:6379>  zadd hit:2 100
item1111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111 20 item2 45 item3
(integer) 3
127.0.0.1:6379>  object encoding hit:2
"skiplist"

 

<think> 我们正在讨论Redis底层存储结构。根据引用内容,Redis底层使用了多种数据结构,包括SDS(简单动态字符串)、链表、字典、跳跃表、整数集合、压缩列表等。此外,Redis并不是直接使用这些数据结构,而是通过个对象系统(redisObject)来包装这些数据结构,从而实现字符串对象、列表对象、哈希对象、集合对象和有序集合对象。 具体来说: 1. SDS(简单动态字符串)是Redis用于存储字符串的底层实现。它通过个结构体来存储字符串,包含长度(len)、已分配空间(alloc)、标志位(flags)和实际字符串数据(buf)。这样的设计使得SDS可以在O(1)时间内获取字符串长度,并且可以避免缓冲区溢出,同时减少内存重分配次数(通过预分配和惰性空间释放)[^1][^2][^4]。 2. 引用[3]提到,Redis中的每个对象都由redisObject结构表示。该结构包含类型(type)、编码(encoding)、指向底层数据结构的指针(ptr)等属性。这样,Redis可以根据不同的使用场景,为同个对象设置不同的底层数据结构实现,从而优化效率。 3. 对于不同的对象类型,Redis会使用不同的底层数据结构。例如,列表对象在元素较少且元素较小时会使用压缩列表(ziplist)[^5],否则会使用双向链表或其他结构。 因此,Redis底层存储结构的核心是:基于对象(redisObject)的多态性,每种对象类型可以对应不同的底层数据结构实现。 下面我们详细说明: </think> ### Redis底层存储结构详解 Redis的底层存储结构采用**对象系统(redisObject)** 整合多种高效数据结构,根据数据类型和场景动态选择最优实现[^3]。核心架构如下: --- #### **1. 对象系统(redisObject)** 所有Redis数据都封装在`redisObject`结构体中,实现多态和自动优化: ```c typedef struct redisObject { unsigned type:4; // 数据类型(字符串/列表/哈希等) unsigned encoding:4; // 底层数据结构编码(如SDS/压缩列表) void *ptr; // 指向实际数据结构的指针 int refcount; // 引用计数(内存回收) unsigned lru:22; // 最近访问时间(LRU淘汰) } robj; ``` - **动态适配**:相同数据类型(如列表)可根据元素数量切换底层实现(压缩列表→双向链表)[^5] - **内存管理**:`refcount`实现引用计数垃圾回收,`lru`支持LRU淘汰策略[^3] --- #### **2. 核心底层数据结构** | 数据类型 | 可能使用的底层结构 | 特点 | |----------------|---------------------------------|----------------------------------------------------------------------| | **字符串** | SDS(简单动态字符串) | O(1)复杂度获取长度,避免缓冲区溢出[^1][^4] | | **列表** | 压缩列表(ziplist)或双向链表 | 小元素用压缩列表(内存紧凑),大元素转链表[^5] | | **哈希** | 压缩列表或字典(hashtable) | 字段少时用压缩列表,字段多时转哈希表 | | **集合** | 整数集合(intset)或哈希表 | 纯整数且元素少时用intset,否则用哈希表 | | **有序集合** | 压缩列表或跳跃表(skiplist) | 元素少且小时用压缩列表,否则用跳跃表(支持范围查询) | --- #### **3. 关键数据结构细节** **(1)SDS(简单动态字符串)** ```c struct __attribute__ ((__packed__)) sdshdr { uint8_t len; // 当前字符串长度 → O(1)获取长度 uint8_t alloc; // 已分配空间大小 → 预分配减少内存重分配[^4] unsigned char flags; // 类型标志 char buf[]; // 柔性数组存储实际数据 }; ``` - **优势**: - 通过 $alloc - len$ 计算剩余空间,自动扩容[^4] - 二进制安全(可存储任意数据,含`\0`) **(2)压缩列表(ziplist)** - **结构**:连续内存块,包含: ```bash <zlbytes><zltail><zllen><entry1>...<entryN><zlend> ``` - **适用场景**:元素数量 $\leq 128$ 且单个元素 $\leq 64\text{字节}$[^5] - **优势**:内存紧凑,减少碎片(适合小数据集) **(3)跳跃表(skiplist)** - **用于有序集合**:支持 $O(\log n)$ 复杂度的范围查询 - **多层索引结构**: $$ \text{查找路径长度} \approx \log_{\frac{1}{p}}n \quad (p=\text{层数概率}) $$ --- #### **4. 数据类型与编码映射** | 数据类型 | 底层编码(encoding) | 触发转换条件 | |----------------|-------------------------------|---------------------------------------| | 字符串 | RAW(SDS) / EMBSTR(短字符串) | EMBSTR用于 $\leq 44\text{字节}$ 的字符串 | | 列表 | ZIPLIST / LINKEDLIST | 元素数量或大小超阈值 → 转链表[^5] | | 哈希 | ZIPLIST / HASHTABLE | 字段数量 $\geq 512$ 或字段值过大 | | 集合 | INTSET / HASHTABLE | 元素含非整数 或 数量 $\geq 512$ | | 有序集合 | ZIPLIST / SKIPLIST | 元素数量 $\geq 128$ 或 元素过大 | > **设计哲学**:空间与时间的trade-off,小数据优先内存效率(压缩列表),大数据优先操作性能(跳跃表/字典)[^2][^5]。 --- #### **总结** Redis存储结构核心特点: 1. **对象封装**:统通过`redisObject`管理数据类型和编码 2. **动态切换**:根据数据规模自动选择最优底层结构 3. **空间优化**:压缩列表/SDS减少内存碎片 4. **性能分层**:小数据极致压缩,大数据高效索引
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值