本文一起看下redis提供的事务功能。
A(Atomic)原子性,C(Consitency)一致性,I(Isolation)隔离性,D(Durability)持久性,其具体要求如下:
A:事务中所有的操作必须是原子的,要么全部执行,要么全部不执行。
C:事务执行前后,数据的状态不发生改变。
I:事务之间的数据不能相互影响,即不能同时修改相同的数据。
D:事务执行后,修改是持久的,即会保存到磁盘,保证不丢失。
一个完备的事务机制必须满足以上的四个特性,但也并不是必须全部满足。那么Redis事务针对这四个特性的满足情况如何呢,我们一起来看下。
Redis通过multi,exec来支持事务,执行multi后代表开始一个事务,接着我们就可以执行各种Redis指令,这些指令会被存储到一个队列中,等到执行exec时一起执行(可以保证以原子的方式执行)
,如下:
A,Atomic,原子性,我们需要通过几种情况来分析。
假如事务队列中的所有命令都能正确执行,Redis能保证其执行的原子性。
如果是入队时Redis就发现命令错误了,比如使用了一个不存在的指令PUT,当执行exec时,Redis会拒绝执行事务队列中的命令,如下:
127.0.0.1:6379> get a:stock
"1"
127.0.0.1:6379> get b:stock
"100"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr a:stock
QUEUED
127.0.0.1:6379> incr b:stock
QUEUED
127.0.0.1:6379> PUT k v
(error) ERR unknown command 'PUT'
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get a:stock
"1"
127.0.0.1:6379> get b:stock
"100"
可以看到事务并没有执行,a:stock和b:stock的值也并没有改变。此时满足原子性。
有些命令,入队时Redis无法发现错误,但是执行时会错误,比如LPOP操作错误的数据类型,如下:
127.0.0.1:6379> get a:stock
"0"
127.0.0.1:6379> get b:stock
"100"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr a:stock
QUEUED
127.0.0.1:6379> lpop b:stock
QUEUED
127.0.0.1:6379> exec
1) (integer) -1
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get a:stock
"-1"
可看到lpop b:stock
执行错误,但是队列中其他指令正常执行了,此时不符合原子性。虽然有错误,Redis也没有回滚,因为Redis并没有提供回滚机制,虽然Redis没有提供事务回滚机制,但是却提供了discard命令,用来主动放弃事务,类似于MySQL的rollback,但只是一种主动的操作,如下:
127.0.0.1:6379> get a:stock
"-1"
127.0.0.1:6379> get b:stock
"100"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decr a:stock
QUEUED
127.0.0.1:6379> incr b:stock
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get a:stock
"-1"
127.0.0.1:6379> get b:stock
"100"
C,Consistency,一致性,分成以下几种情况分析。
此时因为所有的命令都正确执行了,所以能够保证一致性。
入队时发现命令,事务队列中的所有命令都不会执行,此时能够保证一致性。
执行命令时发现命令错误,此时,部分命令执行了,错误命令没有执行,不能保证一致性。
I,Isoation,隔离性,需要配合watch机制才能实现,watch机制的意思是,事务执行前,先监听某个key,在exec执行时,如果被监听的key发生了变化,则事务会放弃执行,如下:
此时隔离性是得到了保证的,因为相同的数据没有同时被不同的事务修改。如果是不使用watch机制的话,相同的值会同时被不同客户端修改,此时不具备隔离性,如下:
D,Durability,持久性,如果是Redis没有开启rdb或aof的话,不具备持久性,因为数据只会在内存,重启就丢失。然后看下开启了rdb或aof 的情况。
事务执行后,并不会立即生成快照,此时数据库宕机,数据会丢失,不具备持久性。
appendfsync当配置为no,everysec时,事务执行后如果发生了数据库宕机,数据会丢失,不具备持久性。当配置为always时,数据其实也并不是直接写到aof文件中,而是先写到内存中,后续再同步到aof文件,因此,此时也不具备持久性。总结就是开启aof也不能让Redis具备持久性。
从前面的分析,Redis事务使用到的命令如下:
支持情况如下:
原子性:支持,当事务队列中的命令都正确时,redis会以原子的方式来执行。
一致性:支持,只有当命令执行时才发现错误时不支持,但是存在支持的场景我们就可以认为其支持。
隔离性:支持,需要配合watch机制。
持久性:不支持,但是redis作为内存数据库,这项不支持,其实影响不大。
参考文章列表:
事务的【ACID】四大原则 。