redis 五大对象类型及底层实现原理分析

在Redis中,并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这些对象系统也就是前面说的五大数据类型,每一种数据类型都至少用到了一种数据结构。通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型判断一个对象是否可以执行给定的命令,而且可以针对不同的场景,为对象设置多种不同的数据结构,从而优化对象在不同场景下的使用效率。

 

1、对象的类型与编码

  Redis使用前面说的五大数据类型来表示键和值,每次在Redis数据库中创建一个键值对时,至少会创建两个对象,一个是键对象,一个是值对象,而Redis中的每个对象都是由 redisObject 结构来表示:

1

2

3

4

5

6

7

8

9

10

11

12

13

typedef struct redisObject{

     //类型

     unsigned type:4;

     //编码

     unsigned encoding:4;

     //指向底层数据结构的指针

     void *ptr;

     //引用计数

     int refcount;

     //记录最后一次被程序访问的时间

     unsigned lru:22;

 

}robj

①、type属性

  对象的type属性记录了对象的类型,这个类型就是前面讲的五大数据类型:

  

  可以通过如下命令来判断对象类型:

1

type key

  

  注意:在Redis中,键总是一个字符串对象,而值可以是字符串、列表、集合等对象,所以我们通常说的键为字符串键,表示的是这个键对应的值为字符串对象,我们说一个键为集合键时,表示的是这个键对应的值为集合对象。

②、encoding 属性和 *prt 指针

  对象的 prt 指针指向对象底层的数据结构,而数据结构由 encoding 属性来决定。

  

  而每种类型的对象都至少使用了两种不同的编码:

  

  可以通过如下命令查看值对象的编码:

1

OBJECT ENCODING    key

  比如 string 类型:(可以是 embstr编码的简单字符串或者是 int 整数值实现)

  

<think>好的,用户之前已经询问了Redis的常见数据结构及其应用场景,现在他们进一步希望了解每个数据结构底层实现原理。我需要确保每个结构的解释清晰且详细,同时保持回答的结构化。 首先,回顾之前的回答,用户可能已经对每个数据结构有了基本了解,现在需要深入底层。我需要逐个分析每种数据结构实现,比如String可能使用SDS,List可能涉及双向链表或压缩列表,Hash可能使用哈希表或ziplist,等等。 接下来,我需要确认每个数据结构底层结构在不同版本中的变化,例如Redis 7.0中List是否仍使用quicklist,或者是否有新的结构引入。同时,要注意到配置参数如ziplist的条目限制,这些会影响内部编码方式。 然后,用户要求每个数据结构单独回复一次消息,但根据当前平台限制,可能需要在一个回答中分点说明。我需要按照之前的列表顺序,逐一详细解释每个数据结构实现原理,包括数据结构、编码方式、内存优化策略等。 同时,需要保持语言简洁,避免过于技术化的术语,但又要准确。例如,解释SDS时,要提到其优势,如O(1)长度获取和二进制安全。对于ziplist和hashtable的区别,要说明在不同数据量下的自动转换。 此外,用户可能对性能优化感兴趣,比如为什么使用跳跃表而不是平衡树,或者HyperLogLog如何实现高效基数估算。需要简要提及这些内容,但不过于深入算法细节。 最后,确保每个部分结构清晰,使用标题和子项,帮助用户快速浏览。检查是否有遗漏的数据结构,如扩展结构中的位图、GEO等,确保覆盖全面。同时,提醒用户底层实现可能因版本而异,建议参考官方文档,显示回答的严谨性。</think>好的,我逐个介绍 Redis 数据结构底层实现原理: --- ### 一、字符串(String) **底层结构**:`SDS` (Simple Dynamic String) - **内存布局**: ```c struct sdshdr { int len; // 已用字节长度 int free; // 剩余可用字节 char buf[]; // 实际存储数据的柔性数组 }; ``` - **特点**: 1. 预分配空间策略:扩容时按需分配额外空间(类似C++ vector) 2. 二进制安全:通过 `len` 判断字符串结束,而非依赖 `\0` 3. 兼容 C 字符串:SDS 末尾仍保留 `\0`,可直接使用 C 字符串函数 - **编码类型**: - `int`:存储整数时直接使用 long 类型 - `embstr`:短字符串(≤44字节)优化存储 - `raw`:长字符串常规存储 --- ### 二、列表(List) **底层结构**:`quicklist` (Redis 3.2+) - **组成**:双向链表 + ziplist 的混合结构 - 每个节点是一个 `ziplist`(压缩列表) - 通过配置 `list-max-ziplist-size` 控制单个 ziplist 的最大容量 - **ziplist 原理**: - 连续内存块,通过偏移量定位元素 - 每个元素存储前驱长度和自身内容 - 插入/删除操作可能引发连锁更新(需重新计算后续元素位置) --- ### 三、哈希(Hash) **底层结构**:`dict` (哈希表) 或 `ziplist` - **默认编码**: - `ziplist`:当所有键值对的键和值的长度都 ≤ `hash-max-ziplist-value`(默认64字节)且键值对数 ≤ `hash-max-ziplist-entries`(默认512) - `hashtable`:不满足上述条件时自动转换 - **哈希表实现**: - 使用链地址法解决哈希冲突 - 渐进式 rehash:扩容时逐步迁移数据,避免服务阻塞 --- ### 四、集合(Set) **底层结构**:`intset` 或 `hashtable` - **intset 条件**: - 所有元素都是整数 - 元素数量 ≤ `set-max-intset-entries`(默认512) - **intset 细节**: - 有序存储,查找使用二分法 - 自动升级:插入新元素时动态调整存储类型(int16 → int32 → int64) - **hashtable 实现**: - 哈希表存储(类似 Hash 结构),但 value 固定为 NULL --- ### 五、有序集合(Sorted Set) **底层结构**:`ziplist` 或 `skiplist + dict` - **ziplist 条件**: - 元素数量 ≤ `zset-max-ziplist-entries`(默认128) - 每个元素的值的长度 ≤ `zset-max-ziplist-value`(默认64字节) - **skiplist + dict 结构**: 1. **跳跃表(skiplist)**: - 多层链表结构,支持 $O(\log n)$ 复杂度的范围查询 - 每个节点存储元素分值(score)和指向后续节点的指针数组 2. **字典(dict)**: - 存储 `元素 → 分值` 的映射,实现 $O(1)$ 时间复杂度的单元素查询 --- ### 六、扩展数据结构 1. **位图(Bitmap)** - **底层**:String 的二进制位操作 - 通过 `SETBIT` 和 `GETBIT` 操作特定偏移量(offset)的 bit 位 2. **HyperLogLog** - **底层**:使用 16384 个 6bit 寄存器(共 12KB) - 基于概率算法,通过观测数据分布估算基数 3. **地理空间(GEO)** - **底层**:使用 Sorted Set 存储 - 经纬度通过 `geohash` 算法转换为 52 位整数作为 score 4. **流(Stream)** - **底层**:`radix tree`(基数树)结构 - 每个消息条目包含唯一 ID(时间戳+序列号)和多组键值对 --- ### 关键设计思想 1. **内存优化**:小数据优先使用连续内存结构(如 ziplist) 2. **性能权衡**:在时间复杂度和空间利用率间寻找平衡 3. **自动转换**:根据数据量动态切换底层编码方式 4. **惰性删除**:删除操作仅标记空间,后续逐步回收 需要具体某个结构的更详细实现分析吗?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值