1、背景

在数据库相关理论中,一个逻辑工作单元想要成为事务,就必须满足ACID,即原子性一致性隔离性持久性

  • (1)原子性

原子性这个概念其实就是指,一个事务内的所有SQL操作都是一个整体,因此只有所有的SQL操作都完全执行成功,该事务方可以认为提交成功。如果在提交事务过程中某一条SQL语句执行失败,则整个事务必须回滚到事务提交前的状态。

  • (2)一致性

而一致性这个概念则是指,事务在完成的时候,必须要保证所有的数据都保持一致的状态,而落实到数据库的各个组成部分上,则要求开发人员能够保证数据、索引、约束、日志等在事务前后具备一致性。

  • (3)隔离性

隔离性这个概念主要针对并发,其核心思想就是不同的并发事务对数据产生的修改必须是相互隔离的,假设有两个不同的事务A和B并发执行,那么对A来讲,它在执行前的状态只有两种,即B执行前和B执行后。同理,对B来讲同样是如此,这样的特性我们就称为隔离性。

  • (4)持久性

持久性相对简单,是指事务完成以后它对数据的影响是永久性的。

2.Redis中的事务处理

redis事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。

Redis中的事务是可以视为一个队列,即我们可以通过MULTI开始一个事务,这相当于我们声明了一个命令队列。接下来,我们向Redis中提交的每条命令,都会被排入这个命令队列。当我们输入EXEC命令时,将触发当前事务,这相当于我们从命令队列中取出命令并执行,所以Redis中一个事务从开始到执行会经历 开始事务 、 命令入队 和 执行事务 三个阶段。下面是一个在Redis中使用事务的简单示例:

127.0.0.1:6379> MULTI 
OK 
127.0.0.1:6379> SET Book_Name "GIt Pro" 
QUEUED 
127.0.0.1:6379> SADD Program_Language "C++" "C#" "Jave" "Python"  
QUEUED 
127.0.0.1:6379> GET Book_Name 
QUEUED 
127.0.0.1:6379> EXEC 
1) OK 
2) (integer) 4 
3) "GIt Pro" 

我们可以注意到Redis中的事务和通常意义上的事务基本上是一致的,即

  • 事务是由一系列操作组成的单个逻辑工作执行单元。特别地,因为在Redis中命令是存储在一个队列中,所以,事务中的所有命令都会按顺序执行,并且在执行事务的过程中不会被客户端发送的其它命令中断。
  • 事务是一个原子操作,事物中的命令只有两种执行结果,即全部执行或者全部不执行。如果客户端在使用MULTI命令开启事务后因为意外而没有执行EXEC命令,则事务中的所有命令都不会执行。同理,如果客户端在使用MULTI命令开启事务后执行EXEC命令,则事务中的所有命令都会执行。
  • Redis中的事务可以使用DISCARD命令来清空一个命令队列,并放弃对事务的执行。如果命令在入队时发生错误,Redis将在客户端调用EXEC命令时拒绝执行并取消事务,但是在EXEC命令执行后发生的错误,Redis将选择自动忽略。

3.redis事务执行过程

一个事务从开始到执行会经历以下三个阶段

  • 1)开始事务
  • 2)命令入队
  • 3)执行事务
    下面将分别介绍事务的这三个阶段。
1)开始事务

MULTI命令的执行标记着事务的开始:

redis> MULTI
OK

这个命令唯一做的就是, 将客户端的 REDIS_MULTI 选项打开, 让客户端从非事务状态切换到事务状态

2)命令入队

当客户端处于事务状态下时, 所有发送给服务器端的命令都会立即被服务器执行

## 非事务
redis> SET msg "hello moto"
OK

redis> GET msg
"hello moto"

但是, 当客户端进入事务状态之后, 服务器在收到来自客户端的命令时, 不会立即执行命令, 而是将这些命令全部放进一个事务队列里, 然后返回QUEUED, 表示命令已入队:

## 事务
redis> MULTI
OK

redis> SET msg "hello moto"
QUEUED

redis> GET msg
QUEUED

其原理如图2所示

3)执行事务

前面说到, 当客户端进入事务状态之后, 客户端发送的命令就会被放进事务队列里。

但其实并不是所有的命令都会被放进事务队列, 其中的例外就是 EXECDISCARDMULTIWATCH 这四个命令 —— 当这四个命令从客户端发送到服务器时, 它们会像客户端处于非事务状态一样, 直接被服务器执行

如果客户端正处于事务状态, 那么当EXEC命令执行时, 服务器根据客户端所保存的事务队列, 以**先进先出(FIFO)**的方式执行事务队列中的命令: 最先入队的命令最先执行, 而最后入队的命令最后执行。

执行事务中的命令所得的结果会以 FIFO 的顺序保存到一个回复队列中。

当事务队列里的所有命令被执行完之后,EXEC命令会将回复队列作为自己的执行结果返回给客户端, 客户端从事务状态返回到非事务状态, 至此, 事务执行完毕。

4.redis事务命令

redis事务使用了multiexecdiscardwatchunwatch命令,命令的作用如图4所示: