zoukankan      html  css  js  c++  java
  • Redis——事务与乐观锁

    Redis——事务与乐观锁

    事务:要么同时成功,要么同时失败。

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

    Redis事务本质:一组命令的集合。一个事务中的所有命令都会被序列化。在事务执行的过程中,会按照顺序执行,并且不允许被别人干扰的。

    总结:一次性、顺序性、排他性。执行一系列的命令。

    -------- 队列 set set set 执行 --------
    

    Redis事务没有隔离级别的概念。

    所有的命令在事务中,并没有直接被执行。只有发起执行命令的时候才会执行。Exec。

    redis的事务:

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

    正常执行事务

    127.0.0.1:6379> MULTI		# 开启事务
    OK
    127.0.0.1:6379> set k1 v1	# 可以看到现在已经在事务中了,任务已经进入队列了
    QUEUED
    127.0.0.1:6379> set k2 v2	# 命令入队
    QUEUED
    127.0.0.1:6379> get k2		# 命令入队
    QUEUED
    127.0.0.1:6379> exec		# 执行事务
    1) OK
    2) OK
    3) "v2"
    

    放弃事务:DISCARD

    一旦放弃事务,队列中的所有任务全都不会执行了。

    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> DISCARD		# 取消事务
    OK
    127.0.0.1:6379> get k4		# 事务中的命令都不会执行
    (nil)
    

    事务异常

    类似于编译时异常(代码有错误,编译不通过)。事务中的所有命令都不会被执行。

    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> set k1 v1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> xxxget v3	# 错误的命令
    (error) ERR unknown command `xxxget`, with args beginning with: `v3`, 
    127.0.0.1:6379> get k1
    QUEUED
    127.0.0.1:6379> exec		# 执行事务也是报错的,所有的命令都不会被执行
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> get k1
    (nil)
    

    类似于运行时异常,计算(1/0)会报错。如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常。

    127.0.0.1:6379> set k1 "v1"
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> INCR k1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> EXEC	# 虽然执行的时候报错了,但是整个事务依旧正常执行成功了
    1) (error) ERR value is not an integer or out of range
    2) OK
    3) OK
    127.0.0.1:6379> MGET k1 k2
    1) "v1"
    2) "v2"
    

    单条命令是可以保证原子性的,事务是不保证原子性的。

    Redis监控

    悲观锁:

    • 很悲观,认为什么时候都会出问题,无论做什么都会加锁。

    乐观锁:

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

    redis测试监视测试

    正常情况

    127.0.0.1:6379> set money 100
    OK
    127.0.0.1:6379> set out 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 20
    QUEUED
    127.0.0.1:6379> INCRBY out 20
    QUEUED
    127.0.0.1:6379> exec
    1) (integer) 80
    2) (integer) 20
    

    特殊情况

    进程一:

    127.0.0.1:6379> watch money		# 监视money对象
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> DECRBY money 10
    QUEUED
    127.0.0.1:6379> INCRBY out 10
    QUEUED
    

    进程二:

    127.0.0.1:6379> set money 100
    OK
    

    回到进程一:

    127.0.0.1:6379> exec			
    (nil)
    # 执行之前,另外一个进程修改了money这个对象的值,这个时候,就会导致事务执行失败
    

    发现这里返回nil,也就是修改失败。

    测试其他进程修改值,使用watch可以当作redis的乐观锁操作。

    如果发现事务执行失败,就先解锁。需要操作的时候,就重新再监视(watch)

    127.0.0.1:6379> UNWATCH			# 取消监视
    OK
    127.0.0.1:6379> WATCH money		# 获取最新的值,再次监视
    OK
    127.0.0.1:6379> MULTI
    OK
    127.0.0.1:6379> DECRBY money 10
    QUEUED
    127.0.0.1:6379> INCRBY out 10
    QUEUED
    127.0.0.1:6379> exec			# 比对监视的值是否发生了变化,如果变化,执行失败,否则,正常执行
    1) (integer) 90
    2) (integer) 30
    

    如果修改失败,重新获取最新的值在操作就好了。

  • 相关阅读:
    vue项目,百度地图api高亮选取区域,高亮某个地区,行政区域等
    vue 项目, 通知子组件更新,父组件中每次点击按钮重新加载子组件,(重新生成dom 元素)
    洛谷 P1003 铺地毯
    Codeforces Round #582 (Div. 3)
    安科 OJ 1190 连接电脑 (并查集)
    2018年牛客多校寒假 第四场 F (call to your teacher) (图的连通性)
    牛客小白月赛16 A 小石的签到题 ( 博弈)
    牛客小白月赛16 E 小雨的矩阵 ( 暴搜)
    安科 OJ 1054 排队买票 (递归,排列组合)
    牛客小白月赛15 C 表单 ( map 使用)
  • 原文地址:https://www.cnblogs.com/liuhuan086/p/13583810.html
Copyright © 2011-2022 走看看