Redis事务常用命令以及通过watch命令实现乐观锁示例

事务:

Redis的单条命令是支持事务的,但是事务不保证原子性。

Redis没有隔离级别的概念,所有的命令在事务中并没有直接去执行,只有发起执行命令的时候才执行。

Redis事务:

  • 开启事务(multi)
  • 命令入队(…)
  • 执行事务(exec)

**Redis事务特点:**一致性,顺序性,排他性。

执行事务命令:

127.0.0.1:6379> multi           #开启事务
OK
127.0.0.1:6379> set myset v1    #命令入队
QUEUED
127.0.0.1:6379> set myset v2    #命令入队
QUEUED
127.0.0.1:6379> get myset       #命令入队
QUEUED
127.0.0.1:6379> set myset v3    #命令入队
QUEUED
127.0.0.1:6379> exec           #执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379>

放弃事务命令:

127.0.0.1:6379> multi           #开启事务
OK
127.0.0.1:6379> set v1 v1       #命令入队
QUEUED
127.0.0.1:6379> set v2 v2       #命令入队
QUEUED
127.0.0.1:6379> DISCARD         #放弃事务
OK
127.0.0.1:6379> get v1          #取消事务后set的命令没有被执行,对象获取是不到的
(nil)
127.0.0.1:6379>

编译型异常:(代码命令有误),事务中所有的命令都不会被执行。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set v1 v1
QUEUED
127.0.0.1:6379> set v2 v2
QUEUED
127.0.0.1:6379> getset k3        #错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set v3 v3
QUEUED
127.0.0.1:6379> exec             #执行事务会报错,所有的命令是不会被执行的
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>

**运行时异常:**如果队列事务中存在语法错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令则会抛出异常。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> incr k1        #出现语法异常,字符串是不能incr的
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec           #执行事务时incr k1命令就会抛出异常,其他命令正常执行
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v3"
127.0.0.1:6379> get k1         #能正常获取到没抛异常的对象
"v1"
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379>

所以由此看出在redis中事务是不能保证原子性的。

监控:watch
悲观锁:

  • 很悲观,认为什么时候都有可能会出现问题,无论做什么都会加锁!

乐观锁:

  • 很乐观,什么时候都不会出现问题,所以不会上锁!更新数据的时候判断一下在此期间是否有人修改过此数据。
  • 获取version
  • 更新的时候比较version

Redis监视设置:

127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money      #监视money对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec            #事务正常结束。数据期间没有变动,则执行成功!
1) (integer) 800
2) (integer) 200
127.0.0.1:6379>

测试多客户端修改值,使用watch可以当做redis的乐观锁 操作!
客户端1监视money:

127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED

客户端2修改money的数据:

27.0.0.1:6379> get money
"1000"
127.0.0.1:6379> set money 2000
OK
127.0.0.1:6379>

这时候客户端1再执行事务就会失败!

127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec       #由于客户端2修改了money的值,这时候exec就会失败!
(nil)
127.0.0.1:6379>

如果事务执行失败了就unwatch(放弃监视)先解锁,然后再次监视money,获取最新的值。

127.0.0.1:6379> set money 1000
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 200
QUEUED
127.0.0.1:6379> incrby cost 200
QUEUED
127.0.0.1:6379> exec
(nil)
#以上是客户端1监视时执行失败的结果,事务执行失败就放弃监视,然后重新监视该值,
#并获取该对象最新的值
127.0.0.1:6379> unwatch      #放弃监视
OK
127.0.0.1:6379> watch money  #重新监视
OK
127.0.0.1:6379> get money    #查看当前money的值,因为客户端2修改了money为2000
"2000"
127.0.0.1:6379> multi        #开启事务
OK
127.0.0.1:6379> decrby money 300
QUEUED
127.0.0.1:6379> incrby cost 300
QUEUED
127.0.0.1:6379> exec         #执行事务
1) (integer) 1700
2) (integer) 300
127.0.0.1:6379>

redis乐观锁应用场景:商品秒杀

猜你喜欢

转载自blog.csdn.net/nxw_tsp/article/details/107922864
今日推荐