Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)

一、WATCH命令介绍

  • WATCH命令是一个乐观锁(optimistic locking)
  • 功能:它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复

演示案例

  • 以下是一个事务执行失败的例子:

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第1张图片

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第2张图片

  • 在时间T4,客户端B修改了"name"键的值,当客户端A在T5执行EXEC命令时,服务器会发现WATCH监视的键"name"已经被修改,因此服务器拒绝执行客户端A的事务,并向客户端A返回空回复

二、使用WATCH命令监视数据库键(watched_keys字典)

  • 每个Redis数据库都保存着一个watched_keys字典:
    • 字典的键是某个被WATCH命令 监视的数据库键
    • 字典的值则是一个链表,链表中记录了所有监视相应数据库键的客户端
typedef struct redisDb {
    // ...
    //正在被WATCH 命令监视的键
    dict *watched_keys;
    // ...
} redisDb;
  • 通过watched_key s字典,服务器可以清楚地知道哪些数据库键正在被监视,以及哪些客户端正在监视这些数据库键
  • 下图是一个watched_keys字典的示例,从这个watched_keys字典中可以看出:
    • 客户端c1和c2正在监视键"name"
    • 客户端c3正在监视键"age"
    • 客户端c2和c4正在监视键"address"

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第3张图片

  • 通过执行WATCH命令,客户端可以在watched_keys字典中与被监视的键进行关联。举个例子,如果当前客户端为c10086,那么客户端执行以下WATCH命令之后:

  • 上图展示的watched_keys字典将被更新至下图所示的状态,其中用虚线包围的两个c10086节点就是由刚刚执行的WATCH命令添加到字典中的

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第4张图片

三、监视机制的触发(touchWatchKey函数、REDIS_DIRTY_CAS标识

  • 所有对数据库进行修改的命令,比如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的数据库键,如果有的话,那么touchWatchKey函数会将监视被修改键的客户端的REDIS_DIRTY_CAS标识打开表示该客户端的事务安全性已经被破坏
  • touchWatchKey函数的定义可以用以下伪代码来描述:
def touchWatchKey(db, key):
    # 如果键key 存在于数据库的watched_keys 字典中
    # 那么说明至少有一个客户端在监视这个key
    if key in db.watched_keys:
        # 遍历所有监视键key 的客户端
        for client in db.watched_keys[key]:
            # 打开标识
            client.flags |= REDIS_DIRTY_CAS
  • 举个例子,对于下图所示的watched_keys字典来说:
    • 如果键"name"被修改,那么c1、c2、c10086三个客户端的REDIS_DIRTY_CAS标识将被 打开
    • 如果键"age"被修改,那么c3和c10086两个客户端的REDIS_DIRTY_CAS标识将被打开
    • 如果键"address"被修改,那么c2和c4两个客户端的REDIS_DIRTY_CAS标识将被打开

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第5张图片

四、判断事务是否安全

  • 当服务器接收到一个客户端发来的EXEC命令时,服务器会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务:
    • 如果客户端的REDIS_DIRTY_CAS标识已经被打开,那么说明客户端所监视的键当中, 至少有一个键已经被修改过了,在这种情况下,客户端提交的事务已经不再安全,所以服务 器会拒绝执行客户端提交的事务
    • 如果客户端的REDIS_DIRTY_CAS标识没有被打开,那么说明客户端监视的所有键都没有被修改过(或者客户端没有监视任何键),事务仍然是安全的,服务器将执行客户端提交 的这个事务
  • 这个判断是否执行事务的过程可以下面的流程图来描述

Redis(设计与实现):67---事务之WATCH命令(watched_keys字典、touchWatchKey函数、REDIS_DIRTY_CAS标识)_第6张图片

你可能感兴趣的:(Redis(设计与实现))