Redis 中乐观锁的实现
乐观锁与悲观锁
乐观锁与悲观锁是锁的两种不同的实现思路,乐观锁倾向于假定程序的运行不存在线程问题,在更新数据的时候先去看一下有没有人更改过数据,等数据更改后再来加锁。悲观锁与此相反,其假定程序的运行一定会出现线程问题,所以在执行指令之前,先加锁。
Redis 中的乐观锁
Redis 中的乐观锁是通过 WATCH 命令来实现的。WATCH 指令可以监视一个或多个 key,可以在事务开始之前为 key 添加 watch,若在事务执行之前监控的 key 发生了变化,则事务将会被中断。
WATCH 基本操作
-
单客户端正常执行事务
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> watch money # 监控 money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET money 1000 QUEUED 127.0.0.1:6379> SET spend 0 QUEUED 127.0.0.1:6379> DECRBY money 10 QUEUED 127.0.0.1:6379> INCRBY spend 10 QUEUED 127.0.0.1:6379> EXEC # 期间没有任何其他的操作,所以正常执行,与此同时监控自动关闭 1) OK 2) OK 3) (integer) 990 4) (integer) 10 127.0.0.1:6379> watch money OK 127.0.0.1:6379> unwatch # 手动关闭监控,这里不能追加 key,直接关闭整个监控 OK
-
执行事务期间出现其他客户端干扰
- 客户端 1: 开启监控和事务
127.0.0.1:6379> watch money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> DECRBY money 90 QUEUED 127.0.0.1:6379> INCRBY spend 90 QUEUED
- 客户端 2: 改变 money 的值
127.0.0.1:6379> DECRBY money 100 (integer) 890
- 客户端 1: 执行事务失败
127.0.0.1:6379> EXEC # 失败,与此同时监控自动关闭 (nil)
总结
Redis 是通过 watch 指令来添加乐观锁,其原理与 Java 和 mySQL 的原理类似,都是通过一个版本号的概念,在事务执行之前,判断所监视 key 的版本号是否有变化,若无变化,执行事务,若有变化,事务中断。