30.Redis的事务属性
今天我们讲一下Redis的事务机制,也就是按照ACID,原子性,一致性,隔离性,持久性,来进行讲解
首先是原子性,原子性的要求很明确,一个事务中多个操作都必须完成,或者不完成,业务应用使用,在Redis中,Redis提供了MULTI和EXEC两个命令来完成这三个步骤
如果要显式的表示一个事务的开启,对应的命令就是MULTI
然后客户端就会把事务中执行的具体操作发给服务器端,比如GET和SET,服务器端会暂存这些命令,不会立刻执行
之后客户端会给服务器端发送提交事务的命令,让数据库实际执行第二部发送的具体操作,服务器收到EXEC命令,会执行命令队列中所有的命令
对应的使用方式可以展示如下
MULTI
DECR a:stock
EXEC
EXEC之后,就可以看到命令执行的结果了
然后通过MULTI和EXEC命令,可以实现多个操作的共同执行
看起来很美好,但是并不符合原子性
对于传入命令阶段传入的命令,如果语法有问题,那么会在传入阶段就被判断出错,并记录这个错误,在EXEC的时候,Redis会拒绝执行所有命令操作
但是如果传入命令阶段没有错误,Redis会尝试执行EXEC,并且在执行过程中,报错出错的命令,并执行正确的命令,这就违背了事务的原子性
而且对于事务的回滚,Redis虽然提供了DISCARD命令,但是这个命令只是主动放弃事务执行,将暂存的命令队列清空,没法把已经执行的命令回滚
所以Redis提供的所有命令,都只能作用于EXEC之前
然后对于一致性
还是可能出现在EXEC相关命令上
如果命令入队时就报错,那么本身就会放弃执行,保证数据库的一致性
如果是执行时报错了,有错误的命令不会执行,正确的命令会执行,对于上层应用来说,无法保证一致性
如果是执行时实例发生了故障,RDB不会再事务执行的时候进行快照,所以如果出现了故障,必然是事务执行之前的快照,保证了一致性
如果是AOF,事务的提交还没有记录到AOF中,所以利用redis-check-aof可以清除这些操作,数据恢复后是一致的
对于隔离性
事务的隔离性,为了避免在事务提交之前数据就被修改了
我们可以考虑使用Redis的WATCH机制来保证事务的隔离性
Watch机制是在事务执行之前,监控一个或者多个值的变化情况,使用EXEC命令执行的时候,WATCH机制就会检查监控的key释放被其他客户端修改了,如果修改了,就放弃执行,保证隔离性
如果不使用Watch,那么如果有并发操作在事务插入命令中间执行了,就无法保证隔离性了
最后,如果之后在事务EXEC之后有并发操作,由于Redis是单线程模型,故不担心隔离性
最后是持久化
因为Redis是内存数据库,所以数据是否持久化就取决于Redis的持久化配置
我们就分别按照RDB和AOF两种持久化来看Redis提供的持久化
如果是AOF模式,其有三种配置选项,分别是no everysec 和 always,但是无论那种模式,都存在丢数据的可能性(always可以通过上层应用保证来确认持久化)
如果是RDB的话,因为事务执行过程中是不会快照的,也有丢失的可能性
所以Redis的持久化属性是得不到保证的
总结一下,我们说了Redis的事务实现,Redis通过MULTI EXEC DISCARD WATCH四个命令来支持事务,对应的功能如下
对于Redis的ACID
其无法保证一致性和持久性,只能通过某些方式完成原子性和隔离性,所以并不太适合做事务保证
最后一个小问题,执行事务的时候,如果Redis发生故障,Redis使用了RDB机制,原子性能保证吗
只是原子性的话是可以保证的,因为Redis并不会在执行事务期间进行快照,所以RDB只会在完成事务之后进行,对于原子性可以保证,持久性不会保证
对于事务来说,可以考虑配合pipeline使用
如果不使用Pipeline,是每一次都需要一来一回,性能地下
使用pipline的话,可以将所有的命令打包好全部发送到服务端,服务端全部处理完成后返回,这样就减少了来回的网络IO次数,提高操作性能,而且还能保证不会被其他的操作打断,保证了隔离性