redis 事务

redis事务

事务

熟悉关系型数据库,例如MySQL的读者,应该对事务是比较了解的,并且向MySQL这样的事务机制可谓是十分强大!
所谓事务,说白了就是一些命令的组合,这一组命令要么全部执行完毕,要么全部不执行,不会存在执行到一半就结束的状态;举个简单的例子:甲向乙进行转账5元,对应的组合命令如下:
在这里插入图片描述
这一组命令集合就是事务,对于这一批命令最后要么是全部都执行,那么就是甲的余额减少5元,乙的余额增加5元;要么全部不执行,甲、乙的余额都没发生变化;不会出现甲这边减少了5元,但是乙这边余额没有增加5元的情况;这就是事务!同时对于事务这种要么全部执行,要么全部不执行的特性叫做原子性!
在MySQL中对于事务原子性的支持就非常的完美,对于MySQL的事务来说,如果在事务的处理过程中出现了错误,那么MySQL会终止这个事务,然后对这个事务已经进行操作进行回滚!将数据恢复成事务没有开始执行前的样子;但是对于redis中的事务来说,它对于原子性的操作就支持的并不是特别好,因为在redis的事务中,即使事务在执行过程中出现了错误,那么redis不会进行回滚,而是会继续向后执行后续的操作。

至于redis的原子性为什么不带上回滚?
大概与redis高性能的应用场景有关,如果redis设计了回滚操作的话,那么redis势必会花费更多的“精力”来维护回滚操作,可能也会引入类似于事务ID、回滚指针、undolog日志等模块,这势必会复杂化redis的业务逻辑,同时这些模块的数据也是需要内存来进行存储的,这样的话redis能够存储的有效数据就变少了,综上这些,可能是redis事务的原子性不带上回滚的原因。

  1. 原子性: 如果按照MySQL中的原子性来定义的话。那么redis事务的原子性是不存在的,因为对于redis事务来说执行过程中出现错误不会进行回滚,而是继续向后执行。
  2. 一致性:redis事务不具备一致性,因为redis事务在执行过程中出现错误不会进行回滚,那么最后事务结束运行出来的结果是不可预期的,一致性没有得到保证。
  3. 持久性:redis事务不具备持久性,因为redis本身就是内存级别的数据库,数据存储在内存中,即使redis拥有持久化机制,但是这个持久化机制并不是强制的,可以由用户手动关闭,与事务是两个独立的模块,并没有强相关起来,而对于MySQL事务的持久性来说,则是强制进行的,用户无法干预;
  4. 隔离性:redis事务不具备隔离性,因为存在隔离性的条件首先是并发,但是对于redis来说,它本身就是用单线程来处理事务,事务与事务之间的执行过程都是串行的,因此不存在数据不一致的情况,故redis事务不具备隔离性。

开启事务

上面我们讲到了事务的原子性、一致性、持久性、隔离性,接下来我们来讲一讲在redis事务中如何开启一个事务:

在redis中操作事务的指令如下:

  1. mutli命令:开启事务
  2. exec命令:提交事务
  3. discard命令:放弃事务

下面我们来具体操作一个事务看看:
我们就以转账为例,现在有两角色:Tom、Bob ,Tom的卡内余额为15元,Bob的卡内余额为5元,现在我们要求Tom向Bob转账5元,那么最后的结果就是Tom的卡内余额为10元,Bob的卡内余额为10元;
在这里插入图片描述

  1. 使用multi开启事务:
    在这里插入图片描述
  2. 然后在事务中进行转账逻辑的书写:

在这里插入图片描述
经过书写转账逻辑我们发现,decrby Tom 5和incrby Bob 5的返回值都是QUEUED,而不是向平常一样返回命令执行过后的结果,这是因为此时的事务还没有提交,只有我们再次输入exec命令完成事务的提交过后,才能够执行上述的逻辑?
那么在事务提交之前,这些命令存储在哪里?
答: 当客户端和redis服务器进行连接过后,redis服务器会给每个客户端都维护一个队列,这个队列就用来专门存储客户端未提交的事务的命令,当客户端输入exec事务提交命令过后,redis服务器才会将这个队列里面的命令全部取出来一次性执行,并且在执行的过后中,就算有其它客户端的命令到来了,也无法“插入”到这批命令集合中进行执行,只有当这一批命令执行完毕过后,才会去处理下一个客户端的命令。当然了我们如果输入discard命令的话,那么redis服务器会放弃队列中的已经存储好的命令,那么这个事务自然也就被放弃了;

  1. 使用exec命令提交事务,再来观察结果:
    在这里插入图片描述
    通过提交事务过后,我们发现最终Tom的卡内余额变为了10,Bob的卡内余额也变为了10,没有出现Tom卡内余额减少,但是Bob卡内余额不变的情况;

当然,如果我们书写完转账逻辑过后,我们又不想进行转账操作了,那么我们完全可以使用discard命令来放弃本次事务,放弃事务的本质也就是:放弃redis服务器内队列中的命令:
在这里插入图片描述
经过实验,我们发现当我们放弃事务过后,数据库中的数据都没有发生变化。

当然,如果事务中的命令出现了错误,redis的处理机制也不进相同:

  1. 命令错误
    在这里插入图片描述
    像上述这种,在事务中存在语法错误的,客户端检查出来了会给你进行报警,但是还是会将你的命令发送到服务端的队列中进行存储;当我们执行exec命令过后,服务端会从队列中取出所有命令,并且会进行一遍语法检查,如果检查出语法错误,那么这个事务会被放弃,这点可以看出redis还是在进自己的最大可能来保证事务的一致性。
  2. 运行时错误
    在这里插入图片描述
    像上述那种,并没有明显的语法错误,但是存在着逻辑错误,只有当事务被提交,即事务运行起来过后才会发现,但是这时候redis事务即使发现了错误也不会撤销在错误之前所做出的修改,而是一股脑的往后执行,这样的错误其实与redis无关,之所以会出现这样的错误完全是由于我们开发人员写错了redis命令造成的,如果我们正确书写了redis语句,那么redis的事务的原子性是不是就与MySQL的事务的一样了呢?

有些应用场景需要在事务之前,确保事务中的key没有被其它客户端修改过,才执行事务,否则不执行。redis提供了watch命令来解决此类问题,下面我们来看一看两个客户端执行命令的时序:
在这里插入图片描述
从时间上来看,客户端1的set命令先到达服务器,而客户端2的命令后到达服务器,最后的结果应该是key1 222,但是实际情况是,虽然客户端1的命令先到达服务器但是由于客户端1发送的是一个事务,只有当事务被真正提交了,才会被执行,而这个提交命令又是晚于客户端2到达服务器的,因此最后的结果就是:key1 111;

像上述这种,在我事务还没有进行提交之前,事务里面的key就被客户端2修改的情况,我们希望,客户端1能够不执行这个事务,想要达到这样的效果,我们可以用watch来监视一下事务中的key:
在这里插入图片描述
具体操作如下:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南猿北者

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值