教程 > Redis教程 > Redis 命令 阅读:20

Redis 事务

Redis 事务


Redis中的事务可以理解成一个命令队列,该队列中的所有操作是一个整体,要么都执行,要么都不执行。所以我们可以认为Redis中的事务是原子性的。 MULTI, EXEC, DISCARDWATCH四个命令是Redis事务的基础。通过下面两个重要的保证,它允许在一个步骤中执行一组命令。

  • 在一个事务中的所有的命令都被序列化并且顺序执行。它在中间执行的过程中不会被其他的客户端提交的命令所打断。这就保证了这些命令执行的独立性。
  • 收到 EXEC 命令后进入事务执行,事务中的这些命令要么都执行,要么都不执行,所以我们可以认为Redis中的事务是原子性的。

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

  • 开始事务;
  • 命令入队列;
  • 执行事务;

在 Redis 中,通过使用MULTI命令启动事务,然后需要传递应在事务中执行的命令列表,之后整个事务由EXEC命令执行。

示例


下面我们先用MULTI命令开启一个事务,然后在事务中添加几个命令,最后用EXEC命令来执行事务中添加的这些命令。

redis 127.0.0.1:6379> MULTI  
OK  
redis 127.0.0.1:6379> EXEC  
(empty list or set)  
redis 127.0.0.1:6379> MULTI  
OK  
redis 127.0.0.1:6379> SET rediscomcn redis  
QUEUED  
redis 127.0.0.1:6379> GET rediscomcn  
QUEUED  
redis 127.0.0.1:6379> INCR visitors  
QUEUED  
redis 127.0.0.1:6379> EXEC  
1) OK  
2) "redis"  
3) (integer) 1  

在上面的例子中,我们看到了 QUEUED 的字样,这表示我们在用 MULTI 开启事务后,每一个命令都会进入到内存队列中缓存起来,如果出现 QUEUED 则表示我们这个命令成功插入了缓存队列,在执行 EXEC 时,这些命令都会被依次执行。 如果中间的GET rediscomcn 命令执行失败,此时的SET rediscomcn redis已经执行成功,它不会再回滚,而下面的命令INCR visitors也照样会执行。

对于事务的执行来说,如果 redis 开启了 AOF 持久化的话,那么一旦事务被成功执行,事务中的命令就会通过 write 命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了 AOF 持久化,这时 AOF 文件就会出现不完整的情况,这时,我们可以使用 redis-check-aof 工具来修复这一问题,这个工具会将 AOF 文件中不完整的信息移除,确保 AOF 文件完整可用。

事务中的错误


在整个事务期间,可能会遇到两种命令错误:

  • 命令可能无法排队,因此在调用EXEC之前可能会出现错误。 例如,命令可能在语法上是错误的(错误的参数数量,错误的命令名称等),或者可能存在一些严重的情况,例如内存不足情况(如果服务器使用maxmemory配置为具有内存限制) 指示);
  • 调用EXEC后,命令可能会执行失败,例如,因为我们对具有错误值的键执行了操作(如针对字符串值调用列表操作)。

客户端通常通过检查已排队命令的返回值来感知发生在EXEC调用之前的第一类错误:如果命令以QUEUED答复,则它已正确排队,否则Redis返回错误。 如果在排队时出现错误,大多数客户端将中止该事务并将其丢弃。

但是从Redis 2.6.5开始,服务器将记住命令累积期间发生错误,并且将拒绝执行事务,同时在EXEC期间返回错误并自动丢弃该事务。 在Redis 2.6.5之前,仅会执行在事务中成功排队命令,以防客户端直接调用EXEC而不管先前的错误如何。 这种新行为使将事务与流水线混合起来变得更加简单,因此可以一次发送整个事务,稍后再读取所有答复。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> jiyik //一个明显错误的指令
(error) ERR unknown command 'haha'
127.0.0.1:6379> ping
QUEUED
127.0.0.1:6379> exec
//redis拒绝了事务的执行,原因是“之前出现了错误”
(error) EXECABORT Transaction discarded because of previous errors.

相反,EXEC之后发生的错误不会以特殊方式处理:即使在事务期间某些命令失败,也会执行所有其他命令。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set age 10
QUEUED
//age不是集合,所以如下是一条明显错误的指令
127.0.0.1:6379> sadd age 20 
QUEUED
127.0.0.1:6379> set age 30
QUEUED
127.0.0.1:6379> exec //执行事务时,redis不会理睬第2条指令执行错误
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
127.0.0.1:6379> get age
"30" //可以看出第3条指令被成功执行了

最后,我们来说说最后一个指令WATCH,这是一个很好用的指令,它可以帮我们实现类似于“乐观锁”的效果,即CAS(check and set)。

WATCH 本身的作用是监视 key 是否被改动过,而且支持同时监视多个 key,只要还没真正触发事务,WATCH 都会尽职尽责的监视,一旦发现某个 key 被修改了,在执行 EXEC 时就会返回 nil,表示事务无法触发。

127.0.0.1:6379> set jiyik val1
OK
127.0.0.1:6379> watch jiyik //开始监视jiyik
OK
127.0.0.1:6379> set jiyik val2 //在EXEC之前,jiyik的值被修改了
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set jiyik val3
QUEUED
127.0.0.1:6379> get jiyik
QUEUED
127.0.0.1:6379> exec //触发EXEC
(nil) //事务无法被执行

相关命令


下表列出了 Redis 事务的相关命令:

序号 命令 说明
1 DISCARD 取消事务,放弃执行事务块内的所有命令
2 EXEC 执行所有事务块内的命令
3 MULTI 标记一个事务块的开始
4 UNWATCH 取消 WATCH 命令对所有 key 的监视
5 WATCH 监视一个(或多个) key

查看笔记

扫码一下
查看教程更方便