您的位置:首页 > 数据库 > Redis

Redis数据对象及底层编码实现

2016-07-14 19:34 561 查看
最近在研究redis,稍微看了一下huanggz的《redis设计与实现》及源码中的相关实现,感觉作者对于redis确实有一定程序的了解,把问题说明很很清楚。本文算是对下午的学习的一个记录。

作为内存数据库,由于其采用的c语言没有内置相关的数据结构,redis自己实现了几个底层的数据结构,具体包括如下:

1. 简单动态字符串

Redis 没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组,以下简称 C 字符串), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将 SDS 用作 Redis 的默认字符串表示。





2. 双端链表

链表提供了高效的节点重排能力, 以及顺序性的节点访问方式, 并且可以通过增删节点来灵活地调整链表的长度。作为一种常用数据结构, 链表内置在很多高级的编程语言里面, 因为 Redis 使用的 C 语言并没有内置这种数据结构, 所以 Redis 构建了自己的链表实现。





3. 字典

字典, 又称符号表(symbol table)、关联数组(associative array)或者映射(map), 是一种用于保存键值对(key-value pair)的抽象数据结构。



4. 跳跃表

跳跃表(skiplist)是一种有序数据结构, 它通过在每个节点中维持多个指向其他节点的指针, 从而达到快速访问节点的目的。

跳跃表支持平均 O(\log N) 最坏 O(N) 复杂度的节点查找, 还可以通过顺序性操作来批量处理节点。

在大部分情况下, 跳跃表的效率可以和平衡树相媲美, 并且因为跳跃表的实现比平衡树要来得更为简单, 所以有不少程序都使用跳跃表来代替平衡树。

Redis 使用跳跃表作为有序集合键的底层实现之一: 如果一个有序集合包含的元素数量比较多, 又或者有序集合中元素的成员(member)是比较长的字符串时, Redis 就会使用跳跃表来作为有序集合键的底层实现。



5. 整数集合

整数集合(intset)是集合键的底层实现之一: 当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现。





6. 压缩列表

压缩列表(ziplist)是列表键和哈希键的底层实现之一。

当一个列表键只包含少量列表项, 并且每个列表项要么就是小整数值, 要么就是长度比较短的字符串, 那么 Redis 就会使用压缩列表来做列表键的底层实现。







这几个数据结构是redis的类型基础,也是理解redis的命令的重要知识。尽管redis拥有了这么多的adt,但是redis并没有直接使用这些数据结构,而是构建了一个对象系统,核心就是redisObject。

个人觉得作者是采用了面向对象的概念,用c语言实现多态,具体来说就是在redisObject这个类里面设置了type与ptr这两个域,其中type指定了对象的类型,而ptr则指向具体的数据。每当我们在redis数据库新建一个键值对时,我们至少会创建两个对象:

1、键对象:键值对的键,字符串类型

2、值对象:键值对的值,其类型根据需要确定。

redis总结包括5种对象,每个对象都由一个redisObject结构表示。如下:

typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
// 指向底层实现数据结构的指针
void *ptr;
// ...
} robj;


感觉兴趣的读者可以自己去看一下这本书及源代码。下面简单记录一下5对象可能采用的数据结构及一些有用的知识。

1、字符串对象

字符串对象的编码可以是int、embstr及raw。

2、列表对象

列表对象的编码可以是ziplist 或者linkedlist。

3、哈希对象

哈希对象的编码可以是ziplist或者hashtable。

4、集合对象

集合对象的编码可以是intset或者hashtable。

5、有序集合对象

有序集合对象的编码可以是ziplist或者skiplist。

重要知识:

1、redis数据库中的每个键值对的“键”与“值”都是一个redisObject对象;

2、redis共有字符串、列表、哈希、集合及有序集合5种类型的对象,每种类型的对象都有至少两种或者以上的编码方式,不同的编码可以在不同的场景上使用,以优化对象的使用效率,整体上的原则是当数据量比较少时,采用一种编码方式,当数据量比较大时,采用另一种编码方式,后者可以更消耗内存(这点可以通过配置文件实现,但不是全部的条件都可以配置,此外,具体的默认配置在不同版本也不一样)。从这种上也可以看出来redis作者声明的关于redis是追求性能这一点的;

3、服务器在执行某些命令之前,会先检查给定键的类型能否执行指定的命令,而检查一个键的类型就是检查键的值的类型;

4、redis的对象系统带有引用计数实现的内存回收机制,当一个对象不再被使用时,该对象所占用的内存就会被自动释放;

5、redis会共享值为0到9999的字符串对象,本质上是共享值为整数类型的字符串对象;

6、对象会记录自己的最后一次被访问的时间,这个时间可以用于计算对象的空转时间,当需要清理内存空间时可以提供依据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: