在传统关系型数据库中,常用ACID性质来检验事务的安全性和可靠性。
在Redis中,事务总是具有原子性(Atomicity)、一致性(Consistency)、和隔离性(Isolation)的,并且当Redis运行在一些特定的持久化模式下,事务也具有耐久性(Durability)。
事务具有原子性是指,数据库事务中将多个操作看做一个整体来执行,要么执行所有的操作,要么一个操作也不执行。
首先弄清楚 Redis 开始事务 multi 命令后,Redis 会为这个事务生成一个队列,每次操作的命令都会按照顺序插入到这个队列中。
这个队列里面的命令不会被马上执行,直到 exec 命令提交事务,所有队列里面的命令会被一次性,并且排他的进行执行。
对于Redis的事务来说,事务队列中的命令要么都执行,要么就一个也不执行,因此,Redis的事务是具有原子性的。
一下展示一个成功执行的事务,事务中所有命令都被执行:
redis> MUTLI
OK
redis> SET msg "hello"
QUEUED
redis> GET msg
QUEUED
redis> EXEC
1) OK
2) "hello"
与此相反,以下展示一个执行失败的事务,这个事务因命令入队出错而被服务器拒绝执行,事务中的所有命令都不会被执行:
redis> MUTLI
OK
redis> SET msg "hello"
QUEUED
redis> GET
(error) ERR wrong number of arguments for 'get' command
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
Redis的事务和一般关系型数据库事务最大的区别在于Redis不支持事务回滚机制。即使事务队列中的某个命令在执行中出现了错误,整个事务也会继续执行下去,知道事务队列中的命令执行完毕。
下面这个例子,即使RPUSH命令在执行期间出现了错误,事务的后续命令也会持续下去,并且之前的命令也不会受任何影响:
redis> SET msg "hello" # msg键是一个字符串
OK
redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> RPUSH msg "goods bye" "bye bye" # 错误的对字符串键msg执行列表键命令
QUEUED
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
1) (integer) 3
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 3
但原子性又一个特点就是要么全部成功,要么全部失败,也就是我们传统 DB 里面说的回滚。要知道 MySQL 为了能进行回滚是花了不少的代价。
Redis的作者在事务功能文档解释说,不支持事务回滚是因为这种复杂的功能和Redis最求简单高效的设计主旨不相符,并且认为,Redis执行错误通常是编程是上产生的,这种错误只会出现在开发环境中,而很少会出现在实际生产环境中,所以他认为没必要开发Redis事务回滚功能。Redis官网记录处理事务错误的方式,以及不支持回滚的原因。从严格的意义上来说 Redis 并不具备原子性。
事务具有一致性是指,如果数据库在执行事务之前是一致的,那么在执行事务之后,无论事务是否执行成功,数据库也仍一致的。
Redis通过谨慎的错误检测和简单的设计来保证了数据库的一致性。以下介绍三个Redis事务可能出错的地方,并说明Redis是如何妥善的处理这些错误,从而保证了数据的一致性。
如果一个事务在入队过程中,出现了命令不存在,或者命令格式不正确等情况,Redis将拒绝这个事务:
redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> YAHoo
(error) ERR unknown command "YAHoo"
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
服务器拒绝执行入队过程中出错的事务,多以Redis的一致性不会被影响。
redis> SET msg "hello" # msg键是一个字符串
OK
redis> MUTLI
OK
redis> SADD fruit "apple" "banana" "cherry"
QUEUED
redis> RPUSH msg "goods bye" "bye bye" # 错误的对字符串键msg执行列表键命令
QUEUED
redis> SADD alphabet "a" "b" "c"
QUEUED
redis> EXEC
1) (integer) 3
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 3
在执行事务总,错误的命令被识别出来,并且不会影响其他命令,错误的命令不会对数据做任何修改,所以Redis的一致性不会被影响。
无论是采用RDB或者AOF持久化方案,都可以使用RDB文件或者AOF文件进行数据恢复,从而将数据库还原至一个一致的状态。
隔离性是指,数据库中有多个事务并发的执行,各个事务之间不会相互影响,并且在并发状态下执行的事务和串行执行的事务产生的结果是完全相同的。
Redis 因为是单线程操作,所以在隔离性上有天生的隔离机制,当 Redis 执行事务时,Redis 的服务端保证在执行事务期间不会对事务进行中断,所以,Redis 事务总是以串行的方式运行,事务也具备隔离性。
事务的持久性是指,当一个事务执行完毕,执行这个事务所得到的结果被保存在持久化的存储中,即使服务器在事务执行完成后停机了,执行的事务的结果也不会被丢失。
因为Redis事务只不过是简单的用队列包裹了一组Redis命令,Redis并没有为事务提供额外的持久化功能,所以Redis的持久性取决于Redis的持久化模式。
[1] 摘自 黄健宏 《Redis 设计与实现》
[2] 摘自 陈于喆 来源:51CTO技术栈