Redis:我认为Redis的“事务”不能称之为事务!
前言
由于在看了很多大佬关于redis事务的文章,然而并没有寻找到与自己不谋而合的观点。 因此写这篇文章,主要是为了提出自己对Redis事务的观点。
什么是事务

根据百度百科对事务的解释:事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性
那么接下来就从ACID这4个特性出发看一下redis是否能够满足?
首先先介绍一下Redis事务的几个命令
- Multi:开启事务
- Exec:执行事务
- Discard:取消事务
1.原子性(atomicity)
定义:一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
下面分两种情况演示一下( 编译型异常 和 运行时异常 )
第一种:编译型异常
步骤 1.清空所有的key
步骤 2.开启事务
步骤 3.在事务中执行一条正确的命令
步骤 4.在事务中执行一条编译时会报错的命令
步骤 5.提交事务

通过上面的截图可以看出
序号(3)这一步执行的正确命令返回QUEUED,表示这条指令已经进入队列(但是此时这条命令还未执行)
序号(4)这一步执行的编译时会报错的命令直接提示了 error
序号(5)执行exec时可以看到事务被取消
序号(6)查看所有的key,显示为空
结论 1.在事务中发生编译型错误时事务会被取消;2.事务中的所有命令都不会被执行;3.这种情况下可以视为原子性得到了保证; 4.但是实际业务中我们并不会出现很多这种情况,而是会出现很多第二种情况。
第二种:运行时错误
步骤 1.清空所有的key
步骤 2.提前set一个值以作演示
步骤 3.开启事务
步骤 4.在事务中执行一条正确的命令
步骤 5.在事务中执行一条运行时会报错的命令
步骤 6.在事务中执行一条正确的命令
步骤 7.提交事务
步骤 8.查看运行结果

通过上面的截图可以看出
序号(4)、(5)、(6)这三步执行的命令都返回了 QUEUED,表示这三条指令已经进入队列,由于此时并未执行命令,所以序号(5)并未报错
序号(7)这一步执行exec命令时就会将 QUEUED 中的命令按顺序的执行,

可以看到第二条命令报错了(提示value不是integer类型,因为我set focus 程序猿自来也,是一个string类型的值),然后第一条和第三条命令依旧正常执行了。
序号(8)通过查看redis的所有key也可以看到现在有3条数据
结论 1.在事务中发生运行时错误,事务不会被取消;2.事务中的所有命令都会被执行,并且错误命令不会影响到正确命令的执行;3.这种情况下可以视为原子性不能得到保证;
2.一致性(consistency)
定义:事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
下面演示一下某种场景
Example:zhangsan和lisi各有1000块,zhangsan给lisi转账500块,最终zhangsan剩500,lisi则是1500。
步骤 1.清空所有的key
步骤 2.提前set两个值以作演示
步骤 3.开启事务
步骤 4.在事务中执行 zhangsan 减少500 的命令
步骤 5.在事务中执行 lisi 增加500 的命令(但是该命令模拟运行时异常)
步骤 6.提交事务
步骤 7.查看运行结果

通过上面的截图可以看出
序号(2)至(5)这四步的命令都正常执行了
序号(6)这一步执行exec命令时提示 error
序号(7)这一步可以看到结果出现了问题
结论 1.根据上面探究原子性时的结论我们可以预见了该结果;2.其实根据一致性的定义:“一致性与原子性是密切相关的” 也可以猜到不能保证原子性的情况下很难保证一致性;3.这种情况下可以视为一致性不能得到保证;
3.隔离性(isolation)
定义:一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
下面演示一下某种场景
Example:依然是拿zhangsan举例,zhangsan有1000块,事务1中 zhangsan 花费 500 块,事务2中给 zhangsan 存入 1000 块。
步骤 1.清空所有的key
步骤 2.提前set一个值以作演示
步骤 3.开启事务1和事务2
步骤 4.在事务1中执行 zhangsan 减少500 的命令,事务2中执行 zhangsan 增加 1000 的命令
步骤 5.事务2先提交事务,事务1再提交事务
步骤 6.查看事务1中 zhangsan 的钱

通过上面的截图可以看出
结论 1.序号(1)至(6)在redis1和redis2中都正常执行了所有命令,没有任务错误;2.根据事务的隔离性 事务1中 zhangsan 的钱应该为500,但是由于事务2的影响导致 zhangsan 的钱变成了 1500 ;3.这种情况下可以视为隔离性不能得到保证;
4.持久性(durability)
定义:持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响
采用rbd持久化方式做演示,为了演示效果,将rbd持久化方式做了一下修改,如下图
下面演示一下
Example:在事务中set了三个key值,然后将redis服务杀掉,最后重启redis服务查看key是否存在?
步骤 1.清空所有的key
步骤 2.开启事务
步骤 3.set了三个个值
步骤 4.提交事务
步骤 5.将redis服务杀掉
步骤 6.重启redis服务
步骤 7.查看key是否存在

通过上面的截图可以看出
结论 1.序号(1)至(7)在redis中都正常执行了所有命令,没有任务错误;2.根据事务的持久性,set的三个key都应该被保存到磁盘中,但是序号(7)查看时,redis中的key为空 ;3.这种情况下可以视为持久性不能得到保证;
最后总结
通过以上案例的演示,可以看出redis的事务很难保证事务的ACID特性。因此Redis的“事务”能称之为事务么?