在 Redis 的命令中, 用于对键 (key) 进行处理的命令占了很大一部分, 而对于键所保存的值的类型, 键能执行的命令又各不相同.redis
好比说, LPUSH
和 LLEN
只能用于列表键, 而 SADD
和 SRANDMEMBER
只能用于集合键, 等等.数据库
另一些命令, 好比 DEL
、 TTL
和 TYPE
, 能够用于任何类型的键, 可是, 要正确实现这些命令, 必须为不一样类型的键设置不一样的处理方式: 好比说, 删除一个列表键和删除一个字符串键的操做过程就不太同样.数据结构
以上的描述说明, Redis 必须让每一个键都带有类型信息, 使得程序能够检查键的类型, 并为它选择合适的处理方式.函数
好比说, 集合类型就能够由字典和整数集合两种不一样的数据结构实现, 可是, 当用户执行 ZADD 命令时, 他/她应该没必要关心集合使用的是什么编码, 只要 Redis 能按照 ZADD
命令的指示, 将新元素添加到集合就能够了。编码
这说明, 操做数据类型的命令除了要对键的类型进行检查以外, 还须要根据数据类型的不一样编码进行多态处理.spa
为了解决以上问题, Redis 构建了本身的类型系统, 这个系统的主要功能包括:指针
redisObject 是 Redis 类型系统的核心, 数据库中的每一个键、值, 以及 Redis 自己处理的参数, 都表示为这种数据类型.code
/* * Redis 对象 */ typedef struct redisObject { // 类型 unsigned type:4; // 对齐位 unsigned notused:2; // 编码方式 unsigned encoding:4; // LRU 时间(相对于 server.lruclock) unsigned lru:22; // 引用计数 int refcount; // 指向对象的值 void *ptr; } robj;
type、 encoding 和 ptr 是最重要的三个属性.server
type
记录了对象所保存的值的类型, 它的值多是如下常量的其中一个.对象
/* * 对象类型 */ #define REDIS_STRING 0 // 字符串 #define REDIS_LIST 1 // 列表 #define REDIS_SET 2 // 集合 #define REDIS_ZSET 3 // 有序集 #define REDIS_HASH 4 // 哈希表
encoding
记录了对象所保存的值的编码, 它的值多是如下常量的其中一个.
/* * 对象编码 */ #define REDIS_ENCODING_RAW 0 // 编码为字符串 #define REDIS_ENCODING_INT 1 // 编码为整数 #define REDIS_ENCODING_HT 2 // 编码为哈希表 #define REDIS_ENCODING_ZIPMAP 3 // 编码为 zipmap #define REDIS_ENCODING_LINKEDLIST 4 // 编码为双端链表 #define REDIS_ENCODING_ZIPLIST 5 // 编码为压缩列表 #define REDIS_ENCODING_INTSET 6 // 编码为整数集合 #define REDIS_ENCODING_SKIPLIST 7 // 编码为跳跃表
也就是经过 encoding
来对键进行不一样的操做.
ptr
是一个指针, 指向实际保存值的数据结构, 这个数据结构由 type
属性和 encoding
属性决定.
举个例子, 若是一个 redisObject 的 type 属性为 REDIS_LIST, encoding 属性为 REDIS_ENCODING_LINKEDLIST
, 那么这个对象就是一个 Redis 列表, 它的值保存在一个双端链表内, 而 ptr 指针就指向这个双端链表;
另外一方面, 若是一个 redisObject 的 type 属性为 REDIS_HASH
, encoding 属性为 REDIS_ENCODING_ZIPMAP
, 那么这个对象就是一个 Redis 哈希表, 它的值保存在一个 zipmap 里, 而 ptr 指针就指向这个 zipmap.
下图展现了 redisObject 、Redis 全部数据类型、以及 Redis 全部编码方式(底层实现)三者之间的关系:
有了 redisObject 结构的存在, 在执行处理数据类型的命令时, 进行类型检查和对编码进行多态操做就简单得多了.
当执行一个处理数据类型的命令时, Redis 执行如下步骤:
做为例子,如下展现了对键 key 执行 LPOP 命令的完整过程:
值得注意的是:
我下载了两个版本的 Redis, 5.0.5 和 3.0.7.
其中 5.0.5 存在 server.h 中, 3.0.7 存在 redis.h 中.
可使find ./ -name "*" | xargs grep "redisObject"
搜索如下.