为了保证多条命令组合的原子性,Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题,本文介绍Redis事务,Lua在下一篇文章介绍
一、事务概述
简单地说,事务表示一组动作,要么全部执行,要么全部不执行。例如在社交网站上用户A关注了用户B,那么需要在用户A的关注表中加入用户B,并且在用户B的粉丝表中添加用户A,这两个行为要么全部执行,要么全部不执行,否则会出现数据不一致的情况
Redis只提供了四个命令管理事务:
MULTI:用来开启一个事务。开启一个事务之后,输入的命令不会被立即执行,而是进入事务队列中(入队),所以可以看见输入命令的结果显示为“QUEUED”
WATCH:是一个乐观锁。它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否至少有一个已经被修改过了,如果是的话,服务器将拒绝执行事务,并向客户端返回代表事务执行失败的空回复
DISCARD:用于取消本次事务,放弃执行事务块内的所有命令。如果使用了WATCH,DISCARD将取消监视连接监视的所有键
EXEC:提交一个事务
所以Redis的事务比较简单,主要是因为它不支持事务中的回滚特性,同时无法实现命令之间的逻辑关系计算,当然也体现了Redis的“keep it simple”的特性
二、事务演示案例
MULTI+EXEC
下面使用MULTI开启一个事务,并且执行相关操作,最后使用EXEC提交执行事务内的操作
备注:可以看到事务开启之后,每次执行的命令结果都会显示QUEUED,表示命令入队,但是没有被执行
MULTI+DISCARD
下面使用MULTI开启一个事务,并且执行相关操作,最后使用DISCARD终止本次事务,并且事务内的操作全部放弃执行
备注:因为discard已经结束事务了,所以再次输入exec会显示没有匹配的multi
MULTI+WATCH+EXEC
客户端1:先设置一个字符串,键名为key,然后使用watch监听该键。然后开启事务
客户端2:在客户端1事务还未结束的时候,修改key
客户端1:操作key,并提交事务。因为key被其他客户端修改,所以EXEC返回nil,事务没有被执行。然后获取key,key没有被改变
三、事务错误的处理
如果事务中出现错误,那么Reiis的处理机制也不尽相同
①命令错误
如果一个事务在入队命令的过程中,出现了命令不存在,或者命令的格式不正确等情况,那么Redis将拒绝执行这个事务
例如:下面操作错将set写成了sett,属于语法错误,会造成整个事务无法执行,key和counter的值未发生变化:
根据文档记录,在Redis 2.6.5以前的版本,即使有命令在入队过程中发生了错误, 事务一样可以执行,不过被执行的命令只包括那些正确入队的命令,以下这段代码是 在Redis 2.6.4版本上测试的,可以看到事务可以正常执行,但只有成功入队的SET命令 和GET命令被执行了,而错误的YAH000O则被忽略了:
redis> MULTI
OK
redis> SET msg "hello"
QUEUED
redis> YAH000O
(error) ERR unknown command ' YAH000O'
redis> GET msg
QUEUED
redis> EXEC
11 OK
2) "hello"
②运行时错误(执行错误)
有些事务输入的命令没有错误,但是语法或逻辑有错误,这类错误不会被立即检测出来,只有当事务提交时才会被检测出来
即使在事务的执行过程中发生了错误,服务器也不会中断事务的执行,它会继续执行事务中余下的其他命令,并且已执行的命令(包括执行命令所产生的结果)不会被出错的命令影响
因为在事务执行的过程中,出错的命令会被服务器识别出来,并进行相应的错误处理, 所以这些出错命令不会对数据库做任何修改,也不会对事务的一致性产生任何影响
③服务器停机
如果Redis服务器在执行事务的过程中停机,那么根据服务器所使用的持久化模式,可能有以下情况出现:
如果服务器运行在无持久化的内存模式下,那么重启之后的数据库将是空白的,因此数据总是一致的
如果服务器运行在RDB模式下,那么在事务中途停机不会导致不一致性,因为服务器可以根据现有的RDB文件来恢复数据,从而将数据库还原到一个一致的状态。如果找不到可供使用的RDB文件,那么重启之后的数据库将是空白的,而空白数据库总是一致的
如果服务器运行在AOF模式下,那么在事务中途停机不会导致不一致性,因为服务器可以根据现有的AOF文件来恢复数据,从而将数据库还原到一个一致的状态。如果找不到可供使用的AOF文件,那么重启之后的数据库将是空白的,而空白数据库总是一致的
综上所述,无论Redis服务器运行在哪种持久化模式下,事务执行中途发生的停机都不会影响数据库的一致性