1. 概念
Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Reids中,单条命令式原子性执行的,但不保证原子性,且没有回滚。
1.1. 传统事务和Redis事务的区别
特性名称 |
传统事务 |
Redis事务 |
原子性 |
事务是不可分割的最小操作单元,要么全部成功,要么全部失败 |
不保证原子性:事务中如果有一条命令执行失败,其他的命令仍然执行,不保证回滚 |
一致性 |
事务完成时,必须使所有的数据都保持一致状态 |
|
隔离性 |
数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行 |
没有隔离级别的概念:队列中的命令没有提交之前都不会被执行。事务中的所有命令都会被序列化,按顺序的执行,事务在执行过程中不会被其他客户端发来的命令所打断。 |
持久性 |
事务一旦提交或回滚,它对数据库中的数据的改变就是永久的 |
事务提交之后,会根据RDB或者AOF进行持久化到磁盘中 |
1.2. 特点
优点:
- 一次性按顺序执行多个Redis命令,不受其他客户端命令请求影响;
- 事务中的命令要么都执行(命令间执行失败互相不影响),要么都不执行(比如中间有命令语法错误);
缺点:
- 事务执行时,不能保证原子性;
- 命令入队每次都需要和服务器进行交互,增加带宽;
注意:
- 当事务中命令语法使用错误时,最终会导致事务执行不成功,即事务内所有命令都不执行;
- 当事务中命令知识逻辑错误,就比如给字符串做加减乘除操作时,只能在执行过程中发现错误,这种事务执行中失败的命令不影响其他命令的执行。
2. 事务命令
命令 |
解释 |
作用 |
multi |
开启事务 |
设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中 |
exec |
执行事务 |
设定事务的结束位置,同时执行事务,与multi配合使用。 |
discard |
取消事务 |
放弃执行事务块中的所有命令。 |
watch |
监听 |
监视一个或多个键,如果在事务执行之前这些键被其他命令所改动,那么事务将会被打断。 |
unwatch |
取消监听 |
取消所有由 WATCH 命令监视的键。如果不想继续监视某些键,可以使用 UNWATCH 命令来取消监视。 |
注意:
- 加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行。
- 在事务执行过程中,其他客户端提交的命令请求不会插入到事务执行命令序列中,这保证了事务的隔离性。
- 事务提供了批量操作缓存的功能,即在发送 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"
- 通过执行multi命令开始一个事务块。然后,依次执行了四个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。
- 通过执行exec命令来提交事务,一次性执行事务队列中的所有命令。执行结果为每个命令的返回值,即"OK"。
- 通过执行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>
- 通过执行multi命令开始一个事务块。然后,依次执行了两个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。
- 通过执行discard命令来取消事务,放弃执行事务块内的所有命令。执行结果为"OK"。
- 通过执行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>
- 通过执行multi命令开始一个事务块。然后,依次执行了三个set命令,每个set命令执行后返回的结果为"QUEUED",表示该命令已被加入到事务队列中等待执行。然而,在执行第三个set命令时,出现了错误。因为Redis中并没有名为"setq"的命令,所以返回结果为"(error) ERR unknown command ‘setq’"。
- 通过执行exec命令来提交事务,一次性执行事务队列中的所有命令。由于之前已经出现了错误,导致事务被中断,所以执行结果为"(error) EXECABORT Transaction discarded because of previous errors."。
- 通过执行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>