三、redis系列之事务

时间:2024-10-10 14:07:26

1. 绪言

  Redis也提供了事务机制,可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞。但Redis对事务的支持是部分支持,不想关系型数据库,要么都成功要么都失败,Redis可以部分成功部分失败。本篇中,我们来详细所以说redis那些事。

2. Redis事务机制

2.1 事务流程

  Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行,要么都成功,要么都失败。Redis事务的实现需要用到 MULTI 和 EXEC 两个命令,事务开始的时候先向Redis服务器发送 MULTI 命令,然后依次发送需要在本次事务中处理的命令,最后再发送 EXEC 命令表示事务命令结束。一个事务从开始到执行会经历以下三个阶段:

  1)开始事务;

  2)命令入队;

  3)执行事务。

  三、redis系列之事务

  从上图输出中可以看到,当输入MULTI命令后,服务器返回OK表示事务开始成功,然后依次输入需要在本次事务中执行的所有命令,每次输入一个命令服务器并不会马上执行,而是返回”QUEUED”,这表示命令已经被服务器接受并且暂时保存起来,最后输入EXEC命令后,本次事务中的所有命令才会被依次执行,可以看到最后服务器一次性返回了三个OK,这里返回的结果与发送的命令是按顺序一一对应的,这说明这次事务中的命令全都执行成功了。

2.2    事务命令

  在上一小节中,我们使用了MULTI命令和EXEC命令。MULTI命令标志着事务的开始,EXEC命令开始执行事务。从上一小节图片中我们也可以看出,事务中的命令要全部执行完之后才能获取每个命令的结果,但是如果一个事务中的命令B依赖于他上一个命令A的结果的话该怎么办呢?例如电商系统在抢购业务中,先要获取当前库存,才能在当前库存的基础上进行其他操作。这种场合仅仅使用上面介绍的MULTI和EXEC是不能实现的,因为MULTI和EXEC中的命令是一起执行的,并不能将其中一条命令的执行结果作为另一条命令的执行参数。这时候就要用到redis事务机制中的其他命令,下面列出了redis事务机制中所有命令:

命令原型

时间复杂度

命令描述

返回值

MULTI

 

用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。

始终返回OK

EXEC

 

执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。

原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。

DISCARD

 

回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的Keys。

始终返回OK。

WATCH key [key ...]

O(1)

在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执行该事务队列中的所有命令。

始终返回OK。

UNWATCH

O(1)

取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监控的Keys都将自动取消。

始终返回OK。

  下面通过代码尝试使用上述几个命令:

  1)正常执行

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> SET key01 a

QUEUED

127.0.0.1:6379> SET key02 b

QUEUED

127.0.0.1:6379> GET key01

QUEUED

127.0.0.1:6379> SET key03 c

QUEUED

127.0.0.1:6379> EXEC

1) OK

2) OK

3) "a"

4) OK

  2) 取消事务

127.0.0.1:6379> MULTI

OK

127.0.0.1:6379> SET key01 a

QUEUED

127.0.0.1:6379> SET key02 b

QUEUED

127.0.0.1:6379> DISCARD

OK

127.0.0.1:6379> GET key01

(nil)

  可以看到,执行DISCARD命令后,返回了OK,事务被取消,所以再次GET key01的时候返回了nil。

  3)WATCH

  三、redis系列之事务

  命令按图示箭头方向顺序输入并执行,在左侧窗口中用WATCH命令监视key01,然后MULTI命令开始后,在右侧窗口更改了key02的值,所以左侧窗口执行EXEC命令后,返回nil,事务执行失败,事务中的INCR key01 , SET key02 1两条命令都没有执行,所以最后获取key02返回的值是nil,而key01的值也是右侧窗口的赋值。

  4)UNWATCH

   三、redis系列之事务

  按图示箭头顺序输入并执行命令,WATCH监视key01后,用UNWATCH接触监视,开始MULTI事务后,在右侧窗口改变key01的值,然后左侧窗口继续执行事务,发现事务正常执行,事务中获取到的key01的值是在右侧窗口赋值的基础上加1,key02也成功创建。

2.3    Redis事务中的错误

  先来看如下两块代码:

  代码一:

   三、redis系列之事务

  在上述代码块中,先给key01赋一个字符串值,然后在事务中进行整数运算,显然是有误的,但是整个事务除了数值运算那个命令其他命令都成功运行。

  代码块二:

   三、redis系列之事务

  在上述代码块中,事务中出现拼写错误,执行事务后,直接提示失败,没有任何返回值,可以发现,事务中所有命令都没有执行。

  对比上述两个代码块,为什么一个事务成功执行,一个事务执行失败呢?这就涉及到redis事务中的两类失败:

  1运行错误: 运行错误表示命令在执行过程中出现错误,比如用GET命令获取一个散列表类型的键值、对字符型进行数字运算等。这种错误在命令执行之前Redis是无法发现的,所以在事务里这样的命令会被Redis接受并执行。如果事务里有一条命令执行错误,其他命令依旧会执行(包括出错之后的命令)。

  2)语法错误就像上面的例子一样,语法错误表示命令不存在或者参数错误例如参数的数量错误、命令名称错误,这种情况需要区分Redis的版本,Redis 2.6.5之前的版本会忽略错误的命令,执行其他正确的命令,2.6.5之后的版本会忽略这个事务中的所有命令,都不执行,就比如上面的例子。这种错误会导致事务执行失败,事务中所有命令都执行失败。

3. 总结

  本篇介绍了redis中的事务机制,但关于分布式锁的部分并未涉及,之后再补充。

  参考:

  https://www.cnblogs.com/Jason-Xiang/p/5364252.html

  https://blog.****.net/qq_37169817/article/details/78839774