素手烹茶|Redis 事务也叫事务吗?,不支持原子性的( 三 )


watch指令 , 类似乐观锁 , 事务提交时 , 如果key的值已被别的客户端改变 , 比如某个list已被别的客户端push/pop过了 , 整个事务队列都不会被执行 。 (当然也可以用Redis实现分布式锁来保证安全性 , 属于悲观锁)
通过watch命令在事务执行之前监控了多个keys , 倘若在watch之后有任何key的值发生变化 , exec命令执行的事务都将被放弃 , 同时返回Null应答以通知调用者事务执行失败 。
悲观锁
悲观锁(PessimisticLock) , 顾名思义 , 就是很悲观 , 每次去拿数据的时候都认为别人会修改 , 所以每次在拿数据的时候都会上锁 , 这样别人想拿这个数据就会block直到它拿到锁 。 传统的关系型数据库里边就用到了很多这种锁机制 , 比如行锁 , 表锁等 , 读锁 , 写锁等 , 都是在做操作之前先上锁
乐观锁
乐观锁(OptimisticLock) , 顾名思义 , 就是很乐观 , 每次去拿数据的时候都认为别人不会修改 , 所以不会上锁 , 但是在更新的时候会判断一下在此期间别人有没有去更新这个数据 , 可以使用版本号等机制 。 乐观锁适用于多读的应用类型 , 这样可以提高吞吐量 。 乐观锁策略:提交版本必须大于记录当前版本才能执行更新
WATCH命令的实现原理在代表数据库的server.h/redisDb结构类型中 , 都保存了一个watched_keys字典 , 字典的键是这个数据库被监视的键 , 而字典的值是一个链表 , 链表中保存了所有监视这个键的客户端 , 如下图 。
举个例子 , 如果当前客户端为client99 , 那么当客户端执行WATCHkey2key3时 , 前面展示的watched_keys将被修改成这个样子:
在任何对数据库键空间(keyspace)进行修改的命令成功执行之后(比如FLUSHDB、SET、DEL、LPUSH、SADD , 诸如此类) , multi.c/touchWatchedKey函数都会被调用——它会去watched_keys字典 , 看是否有客户端在监视已经被命令修改的键 , 如果有的话 , 程序将所有监视这个/这些被修改键的客户端的REDIS_DIRTY_CAS选项打开: