字符串
字符串类型分别使用 REDIS_ENCODING_INT 和 REDIS_ENCODING_RAW 两种编码:
REDIS_ENCODING_INT 使用 long 类型来保存 long 类型值。
REDIS_ENCODING_RAW 则使用 sdshdr 结构来保存 sds (也即是 char* )、 long long 、
double 和 long double 类型值。
换句话来说, 在 Redis 中, 只有能表示为 long 类型的值, 才会以整数的形式保存, 其他类型的整数、小数和字符串, 都是用 sdshdr 结构来保存。
[外链图片转存失败(img-XhnK28s8-1567048898164)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-bb7ecaf3be8e729365b5a9241cdcb04aa5a709d1.svg)]
新创建的字符串默认使用 REDIS_ENCODING_RAW 编码, 在将字符串作为键或者值保存进数据库时, 程序会尝试将字符串转为 REDIS_ENCODING_INT 编码
哈希表
REDIS_HASH (哈希表)是 HSET 、 HLEN 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_HT 两种编码方式:
[外链图片转存失败(img-MKi6CDzh-1567048898168)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-145365a458984496cfecacd67b29f5d42c39a401.svg)]
创建空白哈希表时, 程序默认使用 REDIS_ENCODING_ZIPLIST 编码, 当以下任何一个条件被满足时, 程序将编码从 REDIS_ENCODING_ZIPLIST 切换为 REDIS_ENCODING_HT :
哈希表中某个键或某个值的长度大于 server.hash_max_ziplist_value (默认值为 64 )。
压缩列表中的节点数量大于 server.hash_max_ziplist_entries (默认值为 512 )
列表
REDIS_LIST (列表)是 LPUSH 、 LRANGE 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_LINKEDLIST 这两种方式编码:
[外链图片转存失败(img-gytpHpeq-1567048898172)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-9d1b937227cf948b8a9bfb3137570475e5407d2c.svg)]
创建新列表时 Redis 默认使用 REDIS_ENCODING_ZIPLIST 编码, 当以下任意一个条件被满足时, 列表会被转换成 REDIS_ENCODING_LINKEDLIST 编码:
试图往列表新添加一个字符串值,且这个字符串的长度超过 server.list_max_ziplist_value (默认值为 64 )。
ziplist 包含的节点超过 server.list_max_ziplist_entries (默认值为 512 )。
列表的阻塞原语BLPOP 、 BRPOP 和 BRPOPLPUSH
阻塞原语并不是一定会造成客户端阻塞:
只有当这些命令被用于空列表时, 它们才会阻塞客户端。
如果被处理的列表不为空的话, 它们就执行无阻塞版本的 LPOP 、 RPOP 或 RPOPLPUSH 命令。
[外链图片转存失败(img-Nfnb66pX-1567048898175)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-657d8e78e1f1357fdff05173a259334670b87f85.svg)]
阻塞
当一个阻塞原语的处理目标为空键时, 执行该阻塞原语的客户端就会被阻塞。
阻塞一个客户端需要执行以下步骤:
将客户端的状态设为“正在阻塞”,并记录阻塞这个客户端的各个键,以及阻塞的最长时限(timeout)等数据。
将客户端的信息记录到 server.db[i]->blocking_keys 中(其中 i 为客户端所使用的数据库号码)。
继续维持客户端和服务器之间的网络连接,但不再向客户端传送任何信息,造成客户端阻塞。
[外链图片转存失败(img-7Srz5418-1567048898186)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-72233dd6a912518ff6874fdad4e20356091a6063.svg)]
当客户端被阻塞之后,脱离阻塞状态有以下三种方法:
被动脱离:有其他客户端为造成阻塞的键推入了新元素。
主动脱离:到达执行阻塞原语时设定的最大阻塞时间。
强制脱离:客户端强制终止和服务器的连接,或者服务器停机。
脱离阻塞状态
通过将新元素推入造成客户端阻塞的某个键中, 可以让相应的客户端从阻塞状态中脱离出来 (取消阻塞的客户端数量取决于推入元素的数量)。
LPUSH 、 RPUSH 和 LINSERT 这三个添加新元素到列表的命令, 在底层都由一个 pushGenericCommand 的函数实现, 这个函数的运作流程如下图:
[外链图片转存失败(img-TbnHSUQy-1567048898188)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-be4975476661a3e683475377d30b659d70bee64c.svg)]
当向一个空键推入新元素时, pushGenericCommand 函数执行以下两件事:
检查这个键是否存在于前面提到的 server.db[i]->blocking_keys 字典里, 如果是的话,
那么说明有至少一个客户端因为这个 key 而被阻塞,程序会为这个键创建一个 redis.h/readyList 结构, 并将它添加到
server.ready_keys 链表中。
将给定的值添加到列表键中。
列表
集合
REDIS_SET (集合)是 SADD 、 SRANDMEMBER 等命令的操作对象, 它使用 REDIS_ENCODING_INTSET 和 REDIS_ENCODING_HT 两种方式编码:
[外链图片转存失败(img-5ykAMOkm-1567048898193)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-2f54a5b62b3507f0e6d579358e426c78b0dfbd5c.svg)]
第一个添加到集合的元素, 决定了创建集合时所使用的编码:
如果第一个元素可以表示为 long long 类型值(也即是,它是一个整数), 那么集合的初始编码为
REDIS_ENCODING_INTSET 。
否则,集合的初始编码为 REDIS_ENCODING_HT 。
集合编码的切换
如果一个集合使用 REDIS_ENCODING_INTSET 编码, 那么当以下任何一个条件被满足时, 这个集合会被转换成 REDIS_ENCODING_HT 编码:
intset 保存的整数值个数超过 server.set_max_intset_entries (默认值为 512 )。
试图往集合里添加一个新元素,并且这个元素不能被表示为 long long 类型(也即是,它不是一个整数)。
集合类型为什么选择intset结构作为其中之一的底层实现方式?
intset底层实现方式是数组,这个数组以有序、无重复的方式保存集合元素,并且根据新添加的整数元素类型来进行自动升级,例如从int16_t升级到int32_t; 它具有灵活性+节省内存的优点;
在集合类型只有整数元素并且元素不是很大的时候选择intset是比较好的一个选择;
字典编码的集合
当使用 REDIS_ENCODING_HT 编码时, 集合将元素保存到字典的键里面, 而字典的值则统一设为 NULL 。
作为例子, 以下图片展示了一个以 REDIS_ENCODING_HT 编码表示的集合, 集合的成员为 elem1 、 elem2 和 elem3 :
[外链图片转存失败(img-gVLWNUdF-1567048898198)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-3b62c50ed475dc6c91adbb8079f0e0104f644931.svg)]
有序集
REDIS_ZSET (有序集)是 ZADD 、 ZCOUNT 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_SKIPLIST 两种方式编码:
[外链图片转存失败(img-Ua7tWSuy-1567048898206)(https://redisbook.readthedocs.io/en/latest/_images/graphviz-4d10098056ec25ed0e239f64bbcac524bce31bc8.svg)]
编码的选择¶
在通过 ZADD 命令添加第一个元素到空 key 时, 程序通过检查输入的第一个元素来决定该创建什么编码的有序集。
如果第一个元素符合以下条件的话, 就创建一个 REDIS_ENCODING_ZIPLIST 编码的有序集:
服务器属性 server.zset_max_ziplist_entries 的值大于 0 (默认为 128 )。
元素的 member 长度小于服务器属性 server.zset_max_ziplist_value 的值(默认为 64 )。
否则,程序就创建一个 REDIS_ENCODING_SKIPLIST 编码的有序集。
有序集合编码的转换
对于一个 REDIS_ENCODING_ZIPLIST 编码的有序集, 只要满足以下任一条件, 就将它转换为 REDIS_ENCODING_SKIPLIST 编码:
ziplist 所保存的元素数量超过服务器属性 server.zset_max_ziplist_entries 的值(默认值为 128 )
新添加元素的 member 的长度大于服务器属性 server.zset_max_ziplist_value 的值(默认值为 64 )