说一说Redis事务是否满足ACID以及WATCH监视命令的作用

说一说Redis事务是否满足ACID以及WATCH监视命令的作用

MySQL关系型数据库InnoDB存储引擎支持事务,Redis这个非关系型数据库也同样实现了事务功能。

Redis 可以通过 MULTIEXECDISCARDWATCH 等命令来实现事务(transaction)功能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2SEF2fa7-1632336077124)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210922212332369.png)]

MULTI命令表示开始事务,之后可以输入多个命令,此时Redis并不会执行这些命令,而是将这些命令入队,当输入EXEC命令时表示执行事务,Redis会从队列中挨个取出命令来执行,执行完所有命令之后统一返回结果。DISCARD命令则是取消一个事务。一个正常的事务执行成功如上图所示,接下来我们就分阶段详细来看看Redis是怎么实现事务的。

开始事务

使用MULTI命令可以将执行该命令的客户端从非事务状态切换到事务状态,而这一切换是通过在客户端状态的flags属性中打开REDIS_MULTI标志位来完成的。MULTI命令的作用你可以类比一下MySQL中的Begin命令。

命令入队

处于事务状态的客户端发送除EXECDISCARDWATCHMULTI这四个命令以外的命令,服务器并不会立即执行发送过来的命令,而是将这个命令放入到一个事务列队中,然后向客户端返回QUEUED,表示该命令已成功入队。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F1eOx8Zy-1632336077129)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210922214655740.png)]

这个事务队列实际上是一个multiCmd类型的数组,数组中每一个multiCmd结构都保存了一个已入队命令的相关信息,包含命令指针,命令参数以及参数数量。事务队列以FIFO先进先出的方式保存入队的命令,执行的时候也就按照入队顺序进行。

在这里插入图片描述

执行事务

当处于事务状态的客户端向服务器发送EXEC命令时,服务器会立即执行这个命令,这个时候服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行结果全部返回给客户端。EXEC命令你可以类比于MySQL的commit命令。

既然Redis实现了事务,那我们就不得不来说一说事务的四大特性,即ACID。

原子性

事务具有原子性指的是,事务中的多个操作要么全部执行,要么全部都不执行。也就是说事务队列中命令那么全部都被服务器执行,那么就全部都不被执行。

下图是所有命令都执行成功,事务执行成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pqU9rOdo-1632336077137)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210922222615279.png)]
我们再来一个事务执行失败的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NNRbfFBZ-1632336077140)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210922233106131.png)]
其中有一条命令因缺少参数而命令执行失败,我们可以看到队列中的所有命令都没有被执行,导致了最后事务也没有执行成功。感觉Redis事务是满足原子性,而且出现错误还进行了回滚操作。

真的是具有原子性吗?再来看下面这张图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS0xe0z3-1632336077142)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210922223035614.png)]

你会发现队列中的3条命令,有2条执行成功了,有1条执行失败了。也就是说**在执行EXEC命令开始执行事务过程中,队列中某些命令执行失败,不会影响到其它命令,队列中其余命令依然可以执行。**所以说,Redis并不支持回滚,因此也就没有实现原子性!

之所以Redis不支持回滚,官方给出解释,说Redis数据库追求的是简单高效,实现回滚操作非常复杂,并且Redis事务执行时出现错误一般都是在开发环境中出现的编程问题,很少会在生成环境中发生,所以官方认为没有必要实现事务的回滚操作。官方说这话明显有甩锅给程序员的嫌疑啊!!!!!!也就说Redis应用场景不是为了数据存储的高可靠而设计的,而是为了数据访问的高性能而设计的,所以为了简单高效放弃了原子性!

换句话说,Redis并没有把执行EXEC命令之前,一些因为语法格式导致的错误划分到“事务失败”这个概念里面,只是相当于取消了事务。而导致Redis在执行事务失败原因,只有在真正执行没有语法错误的命令过程中,出现了数据结构层面的错误,这种情况是不会回滚的。或者事务只执行了一半,Redis发生故障宕机,像这样的情况,Redis也认为事务执行失败,如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误,此时我们可以使用redis-check-aof程序可以修复这一问题,它会移除AOF文件中不完整事务的信息,确保服务器可以顺利启动!我个人认为这样情况,似乎保证了事务的原子性,当然,像是宕机这种情况还是很少发生的!

一致性

我们所说的事务一致性指的是,在事务执行前后,无论事务执行成功还是失败,数据库状态都应该保持一致,也就说在执行完事务之后,数据库会从一个一致性状态转为另外一个一致性状态。

实际上我们说Redis没有实现事务的回滚操作,也就不具有原子性,基本上也就不能保证数据的一致性。

Redis会拒绝命令入队错误的事务,但是命令执行中发生的错误不会引起整个事务失败,其它命令依然可以执行。因此也就无法保证数据一致性。我们所说的数据一致性更多的情况应该依赖于应用层面,不应该依赖于数据库。

隔离性

事务的隔离性指的是,多个事务并发执行,事务之间不会相互影响。因为Redis是使用单线程的方式来执行事务,也就是说事务总是以串行的方式执行的,各个事务不会相互影响,因此Redis事务具有隔离性。

持久性

事务的持久性指的是,事务执行完成所得到的结果,可以保存到磁盘当中,保证服务器宕机或者重启数据不会丢失。Redis事务机制本身不具有持久性,但是可以依赖Redis持久化机制RDB和AOF,来实现事务的持久性。

  • 如果Redis没有开启持久化模式,那么事务也就不具有持久性
  • 如果Redis开启RDB持久化模式,服务器只有在特定的满足保存条件的情况下,才会对数据库数据进行保存,所有并不能保证事务操作的数据及时的保存到磁盘中,因此RDB持久化模式也不能保证事务的持久性。
  • 如果Redis开启AOF持久化模式,只有在同步参数设置为always时,也就是在命令写入AOF文件立即执行同步函数,保证数据及时保存到磁盘中,这样情况下事务才具有持久性!

至此我们讲完了Redis事务的ACID4种特性。但是,我个人认为,我们用关系型数据库事务的ACID特性去衡量非关系型数据库的事务,我觉得这不太合适,是没有意义的!Redis是非关系型数据库,追求的是简单高效,追求的是数据访问的高效能!所以Redis官方设计Redis时也无意去实现具有ACID特性的事务!

Redis针对事务:

  1. 官方所认为的原子性,也就是要么命令都运行,要么命令都不运行。至于是否都正确运行,就不关心了。所以说并不是严格意义上的原子性,因此可以认为Redis事务不具有原子性!
  2. 并没有实现回滚,因此也就不具有原子性,也就不具有一致性,非关系型数据库没有类似关系型数据库那样的约束,在非关系型数据库中谈数据一致性有点“强词夺理”!
  3. 因为Redis是单线程模型,相当于将事务的执行串行化,因此事务具有隔离性!
  4. 依赖于Redis的RDB和AOF持久化模是,可以实现事务部分持久性。只有在AOF持久化模式,并且将同步设置为always,事务才具有持久性。

我们说Redis事务没有实现回滚,不具有原子性、一致性,可以说Redis事务在并发情况下,可靠性和安全性都是得不到保证的。

Redis提供了WATCH命令,它是一个乐观锁,可以在事务开始之前,监视多个数据库键,当事务执行时,检查被监视的键是否被修改过,如果被修改过,服务器就会拒绝执行事务,并向客户端返回null表示事务失败。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5qjIPQr-1632336077144)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210923022352337.png)]

接下来说说WATCH命令是如何实现监视数据库键的。

实际上Redis数据库都保存这一个watched_keys字典,我们说过字典底层一般都是一个哈希表,这个字典的key是某个被watch命令监视的数据库键,字典的value则是一个链表,链表记录了所有监视这个数据库键的客户端。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4oBGukP-1632336077145)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210923022957149.png)]

当对数据库执行修改命令时,就会检查watched_keys字典,看看是否有正在监视的数据库键被修改了,如果有就会将对应的客户端REDIS_DIRTY_CAS标志位打开,表示该客户端执行的事务安全性被破坏。服务器会根据客户端这个标志位是否打开来判断是否拒绝执行这个事务。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TuFhmnh8-1632336077148)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210923023545594.png)]

作为非关系型数据库,Redis设计的初衷是数据访问的高性能,追求读写快、简单高效,所以淡化了事务的ACID的概念,不要求数据存储的高可靠性,同时使用WATCH监视命令,加强了并发场景下事务的安全性!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值