【Redis系列笔记】Redis事务

59 阅读6分钟

1. 概念

Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

Reids中,单条命令式原子性执行的,但不保证原子性,且没有回滚。

1.1. 传统事务和Redis事务的区别

特性名称传统事务Redis事务
原子性事务是不可分割的最小操作单元,要么全部成功,要么全部失败不保证原子性:事务中如果有一条命令执行失败,其他的命令仍然执行,不保证回滚
一致性事务完成时,必须使所有的数据都保持一致状态
隔离性数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行没有隔离级别的概念:队列中的命令没有提交之前都不会被执行。事务中的所有命令都会被序列化,按顺序的执行,事务在执行过程中不会被其他客户端发来的命令所打断。
持久性事务一旦提交或回滚,它对数据库中的数据的改变就是永久的事务提交之后,会根据RDB或者AOF进行持久化到磁盘中

1.2. 特点

优点:

  • 一次性按顺序执行多个Redis命令,不受其他客户端命令请求影响;
  • 事务中的命令要么都执行(命令间执行失败互相不影响),要么都不执行(比如中间有命令语法错误);

缺点:

  • 事务执行时,不能保证原子性;
  • 命令入队每次都需要和服务器进行交互,增加带宽;

注意:

  • 当事务中命令语法使用错误时,最终会导致事务执行不成功,即事务内所有命令都不执行;
  • 当事务中命令知识逻辑错误,就比如给字符串做加减乘除操作时,只能在执行过程中发现错误,这种事务执行中失败的命令不影响其他命令的执行。

2. 事务命令

命令解释作用
multi开启事务设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中
exec执行事务设定事务的结束位置,同时执行事务,与multi配合使用。
discard取消事务放弃执行事务块中的所有命令。
watch监听监视一个或多个键,如果在事务执行之前这些键被其他命令所改动,那么事务将会被打断。
unwatch取消监听取消所有由 WATCH 命令监视的键。如果不想继续监视某些键,可以使用 UNWATCH 命令来取消监视。

注意:

  1. 加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行。
  2. 在事务执行过程中,其他客户端提交的命令请求不会插入到事务执行命令序列中,这保证了事务的隔离性。
  3. 事务提供了批量操作缓存的功能,即在发送 EXEC 命令前,所有操作都会被放入队列缓存。

3. 事务使用

3.1. 正常执行

192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> set aa AA
QUEUED
192.168.xxx.21:6379> set bb BB
QUEUED
192.168.xxx.21:6379> set cc CC
QUEUED
192.168.xxx.21:6379> set dd DD
QUEUED
192.168.xxx.21:6379> exec
1) OK
2) OK
3) OK
4) OK
192.168.xxx.21:6379> get aa
"AA"
  1. 通过执行multi命令开始一个事务块。然后,依次执行了四个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。
  2. 通过执行exec命令来提交事务,一次性执行事务队列中的所有命令。执行结果为每个命令的返回值,即"OK"。
  3. 通过执行get aa命令获取键"aa"的值,返回结果为"AA"。

3.2. 取消事务

192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> set aa 11
QUEUED
192.168.xxx.21:6379> set ee EE
QUEUED
192.168.xxx.21:6379> discard
OK
192.168.xxx.21:6379> get aa
"AA"
192.168.xxx.21:6379> get ee
(nil)
192.168.xxx.21:6379>
  1. 通过执行multi命令开始一个事务块。然后,依次执行了两个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。
  2. 通过执行discard命令来取消事务,放弃执行事务块内的所有命令。执行结果为"OK"。
  3. 通过执行get aa命令获取键"aa"的值,返回结果为"AA"。而执行get ee命令获取键"ee"的值时,由于之前已经取消了事务,所以返回结果为"(nil)",表示该键不存在。

3.3. 事务队列中存在命令错误

192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> set aa 22
QUEUED
192.168.xxx.21:6379> set bb 33
QUEUED
192.168.xxx.21:6379> setq cc 44
(error) ERR unknown command 'setq'
192.168.xxx.21:6379> set ff FF
QUEUED
192.168.xxx.21:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
192.168.xxx.21:6379> get ff
(nil)
192.168.xxx.21:6379> get bb
"BB"
192.168.xxx.21:6379>
  1. 通过执行multi命令开始一个事务块。然后,依次执行了三个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。然而,在执行第三个set命令时,出现了错误。因为Redis中并没有名为"setq"的命令,所以返回结果为"(error) ERR unknown command ‘setq’"。
  2. 通过执行exec命令来提交事务,一次性执行事务队列中的所有命令。由于之前已经出现了错误,导致事务被中断,所以执行结果为"(error) EXECABORT Transaction discarded because of previous errors."。
  3. 通过执行get ff命令获取键"ff"的值时,由于事务被中断,所以返回结果为"(nil)“,表示该键不存在。而执行get bb命令获取键"bb"的值时,由于事务被中断,所以返回结果为"BB”。

总结:如果在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行

3.4. 事务队列中存在语法错误

192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> incr aa
QUEUED
192.168.xxx.21:6379> set ff FF
QUEUED
192.168.xxx.21:6379> set bb 22
QUEUED
192.168.xxx.21:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
192.168.xxx.21:6379> get bb
"22"
192.168.xxx.21:6379> get ff
"FF"
192.168.xxx.21:6379>

过程同上,但incr aa时由于aa的值为AA,字符串无法加1

总结:如果在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。

3.5. watch监听

192.168.xxx.21:6379> watch aa
OK
192.168.xxx.21:6379> set aa Aa
OK
192.168.xxx.21:6379> get aa
"Aa"
192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> set aa 11
QUEUED
192.168.xxx.21:6379> exec
(nil)
192.168.xxx.21:6379> get aa
"Aa"
192.168.xxx.21:6379>

watch 命令可以监听一个或多个键,一旦有其中一个键被修改(被删除),后面的事务就不会执行了。

当watch监听多个键时,执行某事务希望取消对其中某个键的监听,可以在事务执行前或者执行后使用unwatch声明取消监听。

192.168.xxx.21:6379> get bb
"BBB"
192.168.xxx.21:6379> watch bb
OK
192.168.xxx.21:6379> multi
OK
192.168.xxx.21:6379> unwatch
QUEUED
192.168.xxx.21:6379> set bb 222
QUEUED
192.168.xxx.21:6379> exec
1) OK
2) OK
192.168.xxx.21:6379> get bb
"222"
192.168.xxx.21:6379>