
























原文地址:https://xilidou.com/2018/03/15/redis-object/
前两篇文章介绍了 Redis 的基本数据结构动态字符串,链表,字典,跳跃表,压缩链表,整数集合,但是使用过 Redis 的同学会发现,平时根本没有使用过这些数据结构。 平时使用的数据结构,包括字符串,列表,哈希,集合,还有有序集合。 其实 Redis 的实现是将底层的一种或者几种数据结构进行结合成我们使用的数据结构。
所以今天这篇文章就是要解释 Redis 是怎么实现符串,列表,哈希,集合,还有有序集合的。
对于 Redis 来说使用了 redisObject 来对所有的对象进行了封装:
1 |
|
我们先关注两个参数
type 和 encoding :
1 |
|
所以通过这段代码我们可以知道 Redis 支持的数据类型如下:
| type | 类型 |
|---|---|
| REDIS_STRING | 字符串 |
| REDIS_LIST | 列表 |
| REDIS_SET | 集合 |
| REDIS_ZSET | 有序集合 |
| REDIS_HASH | 哈希表 |
Redis 的 Object 通过 ptr 指向具体的底层数据。Redis 的底层数据:
| 编码 | 类型 |
|---|---|
| REDIS_ENCODING_RAW | SDS 实现的动态字符串对象 |
| REDIS_ENCODING_INT | 整数实现的动态字符串对象 |
| REDIS_ENCODING_HT | 字典实现的 hash 对象 |
| REDIS_ENCODING_ZIPMAP | 压缩map实现对对象,(3.0)版本未使用 |
| REDIS_ENCODING_LINKEDLIST | 双向链表实现的对象 |
| REDIS_ENCODING_ZIPLIST | 压缩列表实现的对象 |
| REDIS_ENCODING_INTSET | 整数集合实现的对象 |
| REDIS_ENCODING_SKIPLIST | 跳跃表实现的对象 |
| REDIS_ENCODING_EMBSTR | 使用 embstr 实现的动态字符串的对象 |
PS:下文会解释 RAW 和 EMBSTR 的区别。
我就按照类型的顺序看看 Redis 是怎么利用底层的数据结构实现不同的对象类型的。
Redis 的字符串 String,主要由 int、raw 和 emstr 底层数据实现的。 Redis 遵循以下的原则来决定使用底层数据结构的使用。
问题来了:
len、free 还有 buf,在3.2之前 len 和 free 的数据类型都是 unsigned int。 这个就是为什么上面的公式 sds header 是 8个字节了。新版本的 sdshdr 变成了 sdshdr8, sdshdr16 和 sdshdr32还有 sdshdr64。优化的地方就在于如果 buf 小,使用更小位数的数据类型来描述 len 和 free 减少他们占用的内存,同时增加了一个char flags。emstr使用了最小的 sdshdr8。 这个时候 sds header 就变成了(len(1b) + free(1b) + flags(1b)) 3个字节, 比之前的实现少了5个字节。 所以新版本的 emstr 的最大字节变成了 44。 还是那句话 Redis 对内存真是 “斤斤计较”Reids 的列表,底层是一个 ziplist 或者 linkedlist。
两个条件都满足使用ziplist编码,两个条件任意一个不满足时,ziplist会变为linkedlist。
3.2 以后使用 quicklist 保存。这个数据结构之前没有讲解过。
实际上 quicklist 是 ziplist 和双向链表结合的产物。我们这样理解,每个双向链表的节点上是一个ziplist。之所以这么设计,应该是空间和时间之间的取舍或者一个折中的方案。 具体的实现我会在以后的文章里面具体分析。
Redis 的集合底层是一个 intset 或者 一个字典(hashtable)。
这个比较容易理解:
使用字典的时候,字典的每一个 key 就是集合的一个元素,对应的 value 就是一个 null。
Redis 的有序集合使用 ziplist 或者 skiplist 实现的。
同时满足以上条件使用ziplist,否则使用skiplist。
对于 ziplist 的实现,redis 使用相邻的两个 entity 分别保存对象以及对象的排序因子。这样对于插入和查询的复杂度都是 O(n) 的。直接看图:

元素开发工程师,排序的因子就是月薪。(好吧php是世界上最好的语言)。
对于skiplist 的实现:
1 |
|
skiplist 的有序链表的实现不只是只有一个 skiplist ,还有一个字典存储对象的key 和 排序因子的映射,这个是为了保证按照key 查询的时候时间负责度为 O(1)。同时有序性依赖 skiplist 维护。大家可以看我之前的教程。所以直接看图:

Redis 的 hash 表 使用 ziplist 和 字典 实现的。
都满足的时候使用 ziplist,否则使用字典。
ziplist 的实现类似,类似 zset 的实现。两个entity成对出现。一个存储key,另一个存储 velue。
还是可以使用上面使用过的图。这个时候 entity 不用排序。key 是职位名称,velue 是对应的月薪。(好吧php还是世界上最好的语言)。与zset实现的区别就是查询是 O(n) 的,插入直接往tail后面插入就行时间复杂度O(1)。
使用字典实现一个 hash表。好像没有什么可以多说的。
这个参数是引用计数。Redis 自己管理内存,所以就使用了最简单的内存管理方式–引用计数。
这个参数记录了对象的最后一次活跃时间。
如果 Redis 开启了淘汰策略,且淘汰的方式是 LRU 的时候,这个参数就派上了用场。Redis 会优先回收 lru 最久的对象。
至此 Redis 的数据结构就介绍完了。
大家可以阅读之前的文章:
Redis 的基础数据结构(二) 整数集合、跳跃表、压缩列表
欢迎关注我的微信公众号:
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。