Redis 13 事务

参考源

【狂神说Java】Redis最新超详细版教程通俗易懂_哔哩哔哩_bilibili

版本

本文章基于 Redis 6.2.6


概述

Redis 事务的本质是一组命令的集合

事务支持一次执行多个命令,一个事务中所有命令都会被序列化。

在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

所以说:Redis 事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

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

批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行。

Redis 事务不保证原子性

Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。

事务中任意命令执行失败,其余的命令仍会被执行。

Redis事务的三个阶段

  1. 开始事务

  2. 命令入队

  3. 执行事务

命令

监听

watch

watch key1 key2 ...

监视一或多个 key,如果在事务执行之前,被监视的 key 被其他命令改动,则事务被打断(类似乐观锁)。

取消监听

unwatch

取消对所有 key 的监控。

标记

multi

标记一个事务块的开始,形成队列(queued)。

执行

exec

执行所有事务块(一旦执行 exec 后,之前加的监控锁都会被取消掉)。

取消

discard

取消事务,放弃事务块中的所有命令。

实践

正常执行

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k2 v2 # 命令入队
QUEUED
127.0.0.1:6379(TX)> get k2 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k3 v3 # 命令入队
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379> get k1 # set命令执行成功
"v1"
127.0.0.1:6379> get k2 # set命令执行成功
"v2"

开启事务后,会出现 TX 标志,此时所有的操作不会马上有结果,而是形成队列(QUEUED),待执行事务后,会将所有命令按顺序执行。

放弃事务

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k2 v2 # 命令入队
QUEUED
127.0.0.1:6379(TX)> set k3 33 # 命令入队
QUEUED
127.0.0.1:6379(TX)> discard # 取消事务
OK
127.0.0.1:6379> get k3 # set命令未执行
"v3"

事务中存在命令性错误

若在事务队列中存在命令性错误(类似于java编译性错误),则执行 exec 命令时,所有命令都不会执行。

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 11 # 命令入队
QUEUED
127.0.0.1:6379(TX)> getset k2 # 错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k2 22 # 命令入队
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务,报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 # set命令未执行
"v1"
127.0.0.1:6379> get k2 # set命令未执行
"v2"

事务中存在语法性错误

若在事务队列中存在语法性错误(类似于 Java 的的运行时异常),则执行 exec 命令时,其他正确命令会被执行,错误命令抛出异常。

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k4 v4 # 命令入队
QUEUED
127.0.0.1:6379(TX)> incr k4 # 命令入队(对“v4”进行 +1 ,会报语法错误)
QUEUED
127.0.0.1:6379(TX)> set k5 v5 # 命令入队
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) (error) ERR value is not an integer or out of range # 执行错误的命令会报错,其余命令正常执行
3) OK
127.0.0.1:6379> get k4 # set命令执行成功
"v4"
127.0.0.1:6379> get k5 # set命令执行成功
"v5"

监听

悲观锁

悲观锁(Pessimistic Lock),顾名思义,就是很悲观。

每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。

这样别人想拿到这个数据就会 block 直到它拿到锁。

传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁。

乐观锁

乐观锁(Optimistic Lock),顾名思义,就是很乐观。

每次去拿数据的时候都认为别人不会修改,所以不会上锁。

但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制。

乐观锁适用于多读的应用类型,这样可以提高吞吐量。

乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

实践

初始化信用卡可用余额和欠额

127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set debt 0
OK

使用 watch 监听 balance,事务期间 balance 数据未变动,事务执行成功。

127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby balance 20
QUEUED
127.0.0.1:6379(TX)> incrby debt 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20

使用 watch 监听 balance,事务期间 balance 数据变动,事务执行失败。

窗口 1:

127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK

窗口 2:

127.0.0.1:6379> get balance
"80"
127.0.0.1:6379> set balance 200
OK

窗口 1:

127.0.0.1:6379(TX)> decrby balance 20
QUEUED
127.0.0.1:6379(TX)> incrby detb 20
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get balance
"200"

由于窗口 1 监听 balance 并开启事务后,窗口 2 修改了 balance 的值,导致窗口 1 的监听失败,执行事务后展示为空,且 balance 的值不是预期值。

监听失败后放弃监听,然后重来

窗口 1:

127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby balance 20
QUEUED
127.0.0.1:6379(TX)> incrby debt 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 180
2) (integer) 40

小结

  • 一旦执行 exec 开启事务后,无论事务是否执行成功, watch 对变量的监听都将被取消。

  • 当事务执行失败后,需重新执行 watch 命令对变量进行监听,并开启新的事务进行操作。

  • watch 指令类似于乐观锁,在事务提交时,如果 watch 监控的多个 key 中任何 key 的值已经被其他客户端更改。

    则使用 exec 执行事务时,事务队列将不会被执行,同时返回 (nil) 应答以通知调用者事务执行失败。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天航星

感谢你的鼓励和认可

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值