likes
comments
collection
share

让你彻底搞懂Redis事务

作者站长头像
站长
· 阅读数 4

🅰️事务ACID特性

  • A 原子性(Atomicity)

原子性指的就是一个事务的操作要么全部成功,要么全部失败。

  • C 一致性(Consistency)

一致性指的是事务执行完成后,数据从一个一致性状态转换为了另一个一致性状态。"一致"指的是数据复合数据库本身的定义和要求,没有包含非法或者无效的错误数据。

  • I 隔离性(Isolation)

事务的隔离性指的是即使数据库中有多个并发事务并发的执行,各个事务之间也不会相互影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全一样。

  • D 持久性 (Durability)

持久性指的就是事务执行完成后,需要把数据进行持久化保存。

🅰️Redis 如何执行事务操作?

Redis 通过 MULTIEXEC 命令执行事务操作,在执行 EXEC提交事务之前,所有的命令都不会执行,会被暂存到队列中,当执行 EXEC 命令提交事务之后,才会从队列中一个个取出来执行。Redis 不支持事务的回滚,但是允许在执行 EXEC 命令提交事务之前通过 DISCARD 命令放弃事务的执行,本质上这个命令就是把队列中等待执行的命令清空。Redis 执行事务操作涉及的命令有 MULTIEXECDISCARD WATCH

  • MULTI 命令:开启事务
  • EXEC 命令:提交事务
  • DISCARD 命令:放弃事务的执行
  • WATCH 命令:监视任意数量的键是否被其他客户端修改

正常执行一个事务操作的例子:

> MULTI   #开启一个事务
"OK"

> SET name zhangsan
"QUEUED"

> SET age 10
"QUEUED"

> EXEC    #提交事务
1) "OK"
2) "OK"

事务提交之前需要放弃事务执行的例子:

> MULTI
"OK"

> SET name zhangsan
"QUEUED"

> SET age 100
"QUEUED"

> DISCARD
"OK"

> EXEX
"ERR unknown command 'EXEX', with args beginning with: "

🅰️Redis 事务能保证原子性吗?

Redis 事务对于原子性的保证需要分情况,不能一概而论

1. 语法错误

发生语法错误也能保证事务的原子性。语法错误指的是在 Redis 通过 MULTI 命令开启事务之后,提交到队列中的命令存在语法错误,那么 Redis 会立马返回错误并放弃事务的执行,即使在之前有语法正确的命令,也会放弃执行。这就保证了事务的原子性。

> MULTI
"OK"

> SET age 100
"QUEUED"

> SET2 age2 100
"ERR unknown command 'SET2', with args beginning with: 'age2' '100' "

> EXEC
"EXECABORT Transaction discarded because of previous errors."

2. 运行错误

发生运行错误无法保证事务的原子性。比如数据库中有一个 String 类型的键 name,Redis 通过 MULTI 命令开启事务之后,使用 LPUSH 操作这个 String 类型的键 name,因为这个操作并没有语法错误且命令还没开始真正执行,只是先加到队列中,所以这个 LPUSH 操作能正常的加入到队列中等待执行,当 Redis 通过 EXEC 命令提交事务时,执行 LPUSH name 就会报错,此时由于前面正确的命令已经执行了,无法放弃,所以就出现一个事务中正确的命令正常执行了,错误的命令无法执行,从而无法保证原子性。

> GET name
"zhangsan"

> MULTI
"OK"

> SET age 100
"QUEUED"

> LPUSH name lisi  #使用LPUSH命令String类型键name
"QUEUED"

> EXEC
1) "OK"
2) "ReplyError: WRONGTYPE Operation against a key holding the wrong kind of value"
> 

3. Redis 宕机

执行事务的 EXEC 命令时 Redis 宕机了,如果开启了 AOF 日志就可以保证事务的原子性。在这种情况下,如果 Redis 开启了 AOF 的持久化方式,那么只会有部分事务操作被记录到 AOF 日志,可以借助 redis-check-aof 工具检查 AOF 日志文件,这个工具可以把未完成的事务操作从 AOF 文件中去除,当我们恢复 Redis 之后,事务操作就不会再被执行,从而保证了原子性。如果没开启 AOF ,只开启了 RDB ,RDB 在事务执行期间是不会执行的,所以 RDB 文件中不会记录事务中只执行的一部分数据结果,之后恢复数据也只是恢复之前的数据,也就跟事务的原子性无关了。

🅰️Redis 事务能保证一致性吗?

1. 语法错误

发生语法错误可以保证 数据库一致性。按照一致性的基本概念:事务执行完成后,数据库从一个一致性状态变为另一个一致性状态。假设事务操作过程中命令出现语法错误,那么 Redis 就会自动放弃事务的执行,这时候数据库的状态是没变化的,所以保证了一致性。

2. 运行错误

发生运行错误可以保证数据库的一致性。在发生运行错误的情况下,正确的命令会被成功执行,错误的命令会执行失败,虽然没法保证原子性,但是数据库确实从一个状态变为了另一个状态,所以这种情况也是能保证数据库的一致性的。

3. Redis 宕机

如果没开启 RDB 或者 AOF 做数据的持久化,也能保证一致性。如果没开启持久化机制,Redis 宕机后重启数据全都没了,那么也是从一个一致性状态到了另一个一致性状态,这个没毛病。

如果开启了 RDB 没开启 AOF ,也能保证一致性。因为 RDB 在事务执行期间是不会执行的,所以 Redis 宕机后重启恢复的数据也是恢复事务之前的数据,自然也就保持了数据库的一致性。

如果开启了 AOF 没开启 RDB ,也能保证一致性。假设在 Redis 发生宕机时,事务操作还没被记录到 AOF 中,那么使用 AOF 日志恢复的数据库数据是一致的;如果部分事务操作已经被记录到 AOF 日志中了,那么使用 redis-check-aof 就可以清除事务中已经完成的操作,数据库恢复后也是一致的。

🅰️Redis 事务能保证隔离性吗?

Redis 使用单线程的方式来执行事务以及事务队列中的命令,并且服务器保证在执行事务期间不会让事务中断,因此 Redis 的事务总是以串行化的方式运行的,并且保证事务也是具有隔离性的。

1. 并发操作在 EXEC 命令执行之前

如果并发操作在 EXEC 命令前,可以通过 WATCH 机制来保证隔离性。WATCH 命令是一个乐观锁,它可以在 EXEC 命令执行之前,监视任意数量的数据库键,并在 EXEC 命令执行时,检查被监视的键是否被其他客户端修改过,如果被修改过,则放弃事务的执行,这样就保证了隔离性。

> WATCH name   #监视name这个键
"OK"

> MULTI
"OK"

> SET name jack
"QUEUED"

> EXEC  #执行EXEC时检查name这个键是否有被其他客户端修改过
1) "OK"

2. 并发操作在 EXEC 命令执行之后

如果并发操作在 EXEC 命令之后,也可以保证隔离性。这是因为 Redis 是单线程执行命令的,EXEC 命令执行之后,Redis 会保证先把队列中的所有命令都执行完才去执行其他的操作,这就使其具备了隔离性。

🅰️Redis 事务能保证持久性吗?

如果没开启 RDB 或者 AOF 持久化,那无法保证事务的持久性,即使开启了持久化机制,也无法保证数据不丢失,所以也无法保证事务的持久性。