关于Redis单机数据库的实现,就要接近尾声,本篇将解析剩下的属性watched_keys和id。
watch命令能在事务开启后执行吗?watch后,事务失败与否,对于一个key需要重新watch么?本篇文章将尝试给出解答。
watched_keys
和之前解析的属性一样,watched_keys仍然是一个字典,它的出现是为了实现Redis事务的相关功能。
考虑这样一个例子:
|
|
客户端01在multi之前,watch了cgrw,之后设置了cgrw;如果在客户端01执行exec之前,客户端02又设置了cgrw,此时再在客户端执行exec,只会返回空。也就是说,通过watch命令,可以保证事务的安全性。
watch触发准备
数据库中的watched_keys扮演了什么角色呢?
形式上,它和blocking_keys属性一样,一个键对应一个链表,两者构成一个键值对并添加到字典,链表里都是watch了这个键的客户端。
watch命令会调用watchCommand(),底层实现的是watchForKey函数,该函数内数据库负责的部分:
|
|
和之前解析blocking_keys相关的部分异曲同工:无则创建链表,有则添加进链表。
同时上篇类似,客户端存在一个watched_keys的同名属性,它的表元素记录key和db,在watchForKey()中还会执行:
|
|
watchedKey可以类比readyList,上一篇已经很详细,就不从头赘述相关代码。
有一点提一下,在watchCommand()内:
|
|
这解释了为什么watch不能在事务中调用。
watch触发
触发源于对touchWatchedKey()的调用:
|
|
前半部分没什么可说的,后面是遍历watch了key的客户端链表,将表示客户端的对象的flags属性设置为REDIS_DIRTY_CAS
状态。
touchWatchedKey()封装在了signalModifiedKey()中,能进行watch触发的命令都会调用signalModifiedKey(),这样的命令太多,基本上只要是能修改key的命令都包括了。
watch触发后响应
客户端输入exec,会调用execCommand(),它会进行如下判断:
|
|
在执行exec后,会判断当前客户端的flags的状态,如果状态为REDIS_DIRTY_CAS
,则事务安全性破坏,最后会调用unwatchAllKeys()。当然,即使状态不为REDIS_DIRTY_CAS
,exec命令最终仍会调用unwatchAllKeys(),用来清空该客户端之前watch的记录。
id
id就是数据库编号啊。
参考
Redis 设计与实现:国内解析Redis的开源资料;