在 Redis 的事务里面,采用的是乐观锁,主要是为了提高性能,减少客户端的等待。由几个命令构成:WATCH, UNWATCH, MULTI, EXEC, DISCARD。
通过 WATCH,可以实现 CAS 操作。使用 WATCH 监听一些键,然后去检查键的值,然后根据键的值来决定是否还需要进行 MULTI,如果键的值被改了,则重新。(因为有可能在执行 WATCH 前,键的值被改了,所以需要先 WATCH,然后再作判断)。在执行 MULTI 命令后,如果中途 WATCH 的键的值被修改了,后续再执行 EXEC 时,整个事务都会被终止。
CAS 使用示例:
假设存在一个 String 类型的状态值, state,需要对其进行 CAS 操作:
- WATCH state
- value = GET state;
- if value == 1
- UNWATCH state
- Return false;
- MULTI
- SET state 1
- result = EXEC
- if result == success
- return true;
- return false;
通过上述的基本应用可以知道,Redis 是通过 WATCH 命令,来保证当前事务的数据是否被修改过,如果被修改了,则整个事务会中止,不再执行。那么,Redis 在实现的时候,会保存对应的 watch key,然后中途如果该 Key 被修改了,则会将对应的所有客户端的标志位都置为 CLIENT_DIRTY_CAS,表示数据被修改,后续执行 EXEC 的时候则会被中断,从而实现事务。而 UNWATCH 命令则是从保存的 watch_keys 里面移除。MULTI 命令仅仅将客户端的标志位 flags 置为 CLIENT_MULTI,表示处于 MULTI 状态,该状态下,后续的命令(除了 MULTI/WATCH/DISCARD/EXEC)外,其它命令都会被保存到一个列表里面,直到 EXEC 或者 DISCARD 命令执行。如果中途出现了语法错误之类的命令,则会将 flags 置为 CLIENT_DIRTY_EXEC。后续执行 EXEC 时,如果 flags 存在 CLIENT_DIRTY_CAS 或者 CLIENT_DIRTY_EXEC,则整个事务会被中止,不执行任何命令。
针对 Redis 的事务实现,对于 ACID,个人认为,对于 Atomicity 和 Durability 以及 Consistency,Redis 是不满足的。为什么会对 ACID 进行分析呢,一部分原因是为了作对比学习,另一部分是因为《Redis 设计与实现》19 章事务 ACID 性质里面提到了一些观点,个人不太认同,所以进行了一些对比。
WATCH 监听 Key,首先就要有地方保存监听的 Key,Redis 针对不同的客户端,会在客户端的结构体里面维护一个 WATCH 监听 Key 的列表,以及在 Server 里面维护一个全局的哈希表,Key 为被监听的 Key,Value 则为一个链表,里面保存了所有监听该 Key 的客户端。如下图:
当执行 WATCH account 时,则首先会判断该 Key 是否已在客户端里面,如果存在,则直接返回,否则加入到客户端对应的 watched_keys 列表里面,然后再将其加入到对应 DB 的 watched_keys 字典表里面,Key 为 account,Value 则为该客户端。
来源: http://www.cnblogs.com/jabnih/p/7118254.html