Redis基础篇 -Redis事务详解及其ACID特性分析

文章目录

  • 1 关于Redis事务
    • 1.1 官网介绍
    • 1.2 解释
  • 2 事务操作
    • 2.1 事务命令
    • 2.3 操作示例
    • 2.3 错误处理
      • 2.3.1 命令排队失败的处理
      • 2.3.2 命令执行失败的处理
  • 3 Redis事务的ACID特性分析
    • 3.1 ACID
    • 3.2 A原子性分析
    • 3.3 C一致性分析
    • 3.4 I隔离性分析
    • 3.5 D持久性分析
  • 参考文档

1 关于Redis事务

1.1 官网介绍

Redis事务(transaction)允许在单个步骤中执行一组命令,并保证了以下两点:

  • 事务中的所有命令都将被序列化并按照顺序执行。
    • 在事务执行的过程中,Redis不会处理其他客户端发送的请求,这保证了这些命令将被当作单个独立操作执行。
  • EXEC命令将触发事务中所有命令的执行。
    • 如果客户端在调用EXEC之前丢失了服务器连接,事务中的所有命令都不会被执行。
    • 相反,如果调用了EXEC,事务中的所有命令都将被执行。
    • 当使用AOF(append-only file)持久化方式时,Redis会确保使用单个写入操作来将事务中的命令保存到磁盘上。

      但是,如果 Redis 服务器崩溃或被系统管理员以某种方式终止,则可能只写入了部分操作。Redis 将在重新启动时检测到这种情况,并会报错退出。使用 redis-check-aof 工具可以修复仅附加文件(删除部分事务),以便服务器可以重新启动。

从Redis 2.2版本开始,Redis使用乐观锁的形式(以类似与CAS的方式)对以上两点提供了额外的保证。

1.2 解释

基本上每种数据库都有自己的数据库事务控制机制,而Redis是将一组命令放到事务中序列化、按顺序一次执行,在执行过程中其他客户端的命令都必须等待事务执行完成。

2 事务操作

2.1 事务命令

Redis事务操作主要涉及以下几个命令:

  • MULTI:标记事务块的开始,后续输入的命令都将排队等待执行。
  • DISCARD:刷新(Flush)事务中排队的所有命令,并将连接从事务状态恢复到正常状态。如果使用了WATCH命令,DISCARD 将取消监视所有被连接(connection)监视(watch)的键。
  • EXEC:执行事务中所有排队的命令,并将连接从事务状态恢复到正常状态。如果使用了WATCH命令,则只会在所有被监视的键都没有被修改的情况下才会执行。
  • WATCH:监视指定的键,以实现事务的有条件执行。
    • WATCH 用于为 Redis 事务提供检查和设置 (CAS) 行为。
    • 取消监视键的方式有如下几种
      1. 调用EXEC,不论是否成功,都将取消对所有键的监视。
      2. 调用DISCARD,将在丢弃事务的同时,取消对所有键的监视。
      3. 使用UNWATCH命令,取消对所有键的监视。
    • 监视键是为了检测执行前被监视的键是否被修改,如果在EXEC之前,有任何一个被监视的键发生了变动(这包括客户端所做的修改(例如写入命令)以及 Redis 本身所做的修改(例如过期或驱逐)),那么整个事务都将被中止,EXEC将返回nil。

    在 6.0.9 之前的 Redis 版本中,过期的密钥不会导致事务中止
    事务中的命令不会触发 WATCH 条件,因为它们只会排队直到发送 EXEC。

2.3 操作示例

示例一:执行事务
使用MULTI开启事务,输入要执行的命令后,使用EXEC来一次执行。
Redis基础篇 -Redis事务详解及其ACID特性分析_第1张图片
示例二:丢弃事务
使用MULTI开启事务,输入命令后,使用DISCARD来丢弃事务。
Redis基础篇 -Redis事务详解及其ACID特性分析_第2张图片
示例三:监视key无变化
使用WATCH来监视一个key,在事务执行前,被监视的key无变化。
Redis基础篇 -Redis事务详解及其ACID特性分析_第3张图片
示例四:监视key被修改
使用WATCH来监视一个key,在事务执行前,key被修改。
Redis基础篇 -Redis事务详解及其ACID特性分析_第4张图片

2.3 错误处理

在Redis事务中,可能出现以下两种错误的情况:

  • 命令排队失败
    • 命令语法错误
    • 一些严重情况,例如内存不足的情况(如果服务器使用 maxmemory 指令配置为具有内存限制)。
  • 命令执行失败
    • 例如,我们对键执行了错误的操作(如对字符串值调用列表操作)。

2.3.1 命令排队失败的处理

Redis 2.6.5开始,如果Redis命令排队过程中检查到错误,将打印错误信息,此后输入的正常命令也会回复“QUEUED”;但是在执行exec命令时,Redis将拒绝执行该事务,返回错误信息并丢弃该事务。

Redis 2.6.5 之前,客户端需要通过检查排队命令的返回值来检测之前发生的错误:如果命令回复 QUEUED,则说明已正确排队,否则 Redis 返回错误。如果在排队命令时出现错误,大多数客户端将中止并丢弃事务。否则,如果客户端选择继续处理该事务,则该EXEC命令将成功执行排队的所有命令,而不管以前的错误如何。

示例:

> multi  ## 开始事务
"OK"

> get name
"QUEUED"

> set name qing.he
"QUEUED"

> ssss name s  ## 语法错误,回复错误信息
"ERR unknown command 'ssss'"

> get name ## 后续正常命令,还是回复QUEUED
"QUEUED"

> exec  ## 拒绝执行、报错、丢弃事务
"EXECABORT Transaction discarded because of previous errors."

2.3.2 命令执行失败的处理

如果命令排队过程中没有错误,那么调用exec命令后出现的错误将不会被特别处理:即使事务中某些命令在执行过程中出现错误,所有其他的命令依旧会被正常执行。

为什么不回滚呢?
官网对此的解释是:
Redis does not support rollbacks of transactions since supporting rollbacks would have a significant impact on the simplicity and performance of Redis.
(Redis不支持事务的回滚,因为回滚将对Redis的简单性和高性能造成重大影响。)

命令执行失败也可大致分为以下两类:

  • 命令本身执行报错:如对键执行了错误的操作(如对字符串值调用列表操作)。
  • Redis故障:EXEC执行过程中,Redis发生故障。

    Redis 将在重新启动时检测到这种情况,并会报错退出。使用 redis-check-aof 工具可以修复仅附加文件(删除部分事务),以便服务器可以重新启动。

示例:

> multi 
"OK"

> get name
"QUEUED"

> set name mr.x
"QUEUED"

> incr name ## 这个命令在执行过程中报错
"QUEUED"

> get name
"QUEUED"

> exec
1) "qing.he" # 错误命令之前的命令正常执行
2) "OK" # 错误命令之前的命令正常执行
3) "ReplyError: ERR value is not an integer or out of range" # 错误命令执行报错
4) "mr.x" # 错误命令之后的命令正常执行

> get name # 验证一下,其他命令确实成功了
"mr.x"

3 Redis事务的ACID特性分析

3.1 ACID

先温习一下数据库事务的ACID:

  • 原子性(Atomicity):原子性要求事务中的所有操作要么全部成功执行,要么全部失败回滚,不允许部分操作成功部分操作失败的情况发生。
  • 一致性(Consistency):一致性要求事务在执行前后数据库应该保持一致状态,即事务执行后数据库应该从一个一致状态转换到另一个一致状态,不会破坏数据库的完整性约束。
  • 隔离性(Isolation):隔离性指多个事务并发执行时,每个事务的操作应该被隔离开,不会相互影响,即每个事务应该感觉不到其他事务的存在。
  • 持久性(Durability):持久性要求事务一旦提交后,其所做的修改应该永久保存在数据库中,即使系统崩溃或发生故障,数据也不应该丢失。

由于Redis事务执行过程中可能出现上文中提到的几种错误,因此对Redis事务是否能够保证ACID特性的分析,也需要根据场景分别讨论。

3.2 A原子性分析

  • 正常情况下
    Redis事务中的所有命令都将被序列化并按照顺序执行,能够保证原子性。
  • 命令排队失败
    事务中所有的命令都会被丢弃,所以能够保证原子性。
  • 命令执行失败
    即使事务中某些命令在执行过程中出现错误,所有其他的命令依旧会被正常执行,因此不能保证原子性。
  • EXEC过程中Redis故障
    如果Redis使用了AOF,将在重新启动时检测到这种情况,并会报错退出。使用 redis-check-aof 工具可以修复仅附加文件(删除部分事务)。这种情况下也可以保证原子性。

3.3 C一致性分析

  • 正常情况下
    Redis事务中的所有命令都将被序列化并按照顺序执行,能够保证一致性。
  • 命令排队失败
    事务中所有的命令都会被丢弃,所以能够保证一致性。
  • 命令执行失败
    有错误的命令不会被执行,正确的命令可以正常执行,也不会改变数据库的一致性。
  • EXEC过程中Redis故障
    在这种情况下,实例故障后会进行重启,这就和数据恢复的方式有关了,我们要根据实例是否开启了 RDB 或 AOF 来分情况讨论下。
    • 如果我们没有开启 RDB 或 AOF,那么,实例故障重启后,数据都没有了,数据库是一致的。

    • 如果我们使用了 RDB 快照,因为 RDB 快照不会在事务执行时执行,所以,事务命令操作的结果不会被保存到 RDB 快照中,使用 RDB 快照进行恢复时,数据库里的数据也是一致的。

    • 如果我们使用了 AOF 日志,而事务操作还没有被记录到 AOF 日志时,实例就发生了故障,那么,使用 AOF 日志恢复的数据库数据是一致的。如果只有部分操作被记录到了 AOF 日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。

3.4 I隔离性分析

  • 在EXEC之前,需要使用WATCH来保证事务的隔离性。
  • 在EXEC之后,由于Redis在事务执行过程中,不会处理其他客户端的请求,因此可以保证隔离性。

3.5 D持久性分析

  • 如果Redis没有配置RDB或AOF持久化,那么肯定时不满足事务的持久性(因为根本不会持久化)。
  • 如果Redis使用了RDB模式,那么在一个事务执行之后,下一次的RDB快照执行前,如果发生了Redis故障,就可能造成事务数据丢失。因此,也不能保证事务的持久性。
  • 如果Redis使用了AOF模式,由于AOF模式的三种配置选项no、everysec、always都存在数据丢失的可能,所以也不能保证持久性。

参考文档

  • Redis官方文档-数据交互-事务
  • Redis事务不支持回滚,你居然还能进行事务控制,牛啊!
  • 腾讯二面:Redis 事务支持 ACID 么?

你可能感兴趣的:(Redis,redis,数据库,缓存)