Redis设计与实现(一~五整合版)(5)

对于一些操作来说,返回值就那几个。对于整数来说,存入的数据也通常不会太大,所以redis通过预分配一些常见的值对象,并在多个数据结构之间(很不幸,你得时指针才能指到这里)共享这些对象,避免了重复分配,节约内存。同时也节省了CPU时间

如图所示:

Screen Shot 2014-09-02 at 23.11.39.png

三个列表的值分别为:

列表 A : [20130101, 300, 10086]

列表 B : [81, 12345678910, 999]

列表 C : [100, 0, -25, 123]

最后一个:redis对对象的管理是通过最原始的引用计数方法。

2. 字符串

字符串是redis使用最多的数据结构,除了本身作为SET/GET的操作对象外,数据库中的所有key,以及执行命令时提供的参数,都是用字符串作为载体的。

在上面的图中,我们可以看见,字符串的底层可以有两种实现:

REDIS_ENCODING_INT使用long类型保存long的值

REDIS_ENCODING_ROW使用sdshdr保存sds、long long、double、long double等

说白了就是除了long是通过第一种存储以外,其他类型都是通过第二种存储滴。

然后新创建的字符串,都会默认使用第二种编码,在将字符串作为键或者值保存进数据库时,程序会尝试转为第一种(为了节省空间)

3. 哈希表

哈希表,嗯,它的底层实现也有两种:

REDIS_ENCODING_ZIPLIST

REDIS_ENCODING_HT(字典)

当创建新的哈希表时,默认是使用压缩列表作为底层数据结构的,因为省内存呀。只有当触发了阈值才会转为字典:

哈希表中某个键或者值的长度大于server.hash_max_ziplist_value(默认为64)

压缩列表中的节点数量大于server.hash_max_ziplist_entries(默认为512)

4. 列表

列表嘛,其实就是队列。它的底层实现也有2种:

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

上面两条的意思很简单,因为POP命令是删除一个节点,那么当没有节点的时候,客户端会阻塞直到一个元素添加进来,然后再执行POP命令,那么,对客户端的阻塞过程是这样的:

将客户端的连接状态更改为“正在阻塞”,并记录这个客户端是被那些键阻塞(可以有多个),以及阻塞的最长时间

将客户端的信息加入到字典server.db[i].blocking_keys中,i就是客户端使用的数据库编号

继续保持客户端和服务器端的连接,但是不发送任何信息,造成客户端阻塞

响应的,解铃须有系铃人:

被动脱离:有其他客户端为造成阻塞的键加入了元素

主动脱离:超过阻塞的最长时间

强制脱离:关闭客户端或者服务器

上面的过程说的很简单,但是在redis内部要执行的操作可以很多的,我们用一段伪代码来演示一下被动脱离的过程:

def handleClientsBlockedOnLists(): # 执行直到 ready_keys 为空 while server.ready_keys != NULL: # 弹出链表中的第一个 readyList rl = server.ready_keys.pop_first_node() # 遍历所有因为这个键而被阻塞的客户端 for client in all_client_blocking_by_key(rl.key, rl.db): # 只要还有客户端被这个键阻塞,就一直从键中弹出元素 # 如果被阻塞客户��执行的是 BLPOP ,那么对键执行 LPOP # 如果执行的是 BRPOP ,那么对键执行 RPOP element = rl.key.pop_element() if element == NULL: # 键为空,跳出 for 循环 # 余下的未解除阻塞的客户端只能等待下次新元素的进入了 break else: # 清除客户端的阻塞信息 server.blocking_keys.remove_blocking_info(client) # 将元素返回给客户端,脱离阻塞状态 client.reply_list_item(element)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/6be4ac5bc3f297e5261a68427fc4282e.html