10.Redis之事务管理

Redis之事务管理

事务简介

Redis事务可以一次执行多个命令,并且带有以下三个重要的保证:

  • 批量操作在EXEC命令前被放入队列缓存。
  • 收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

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

  1. 开启事务
  2. 命令入列
  3. 执行事务

事务相关命令

序号 命令 描述
1 discard discard 命令用于取消事务,放弃执行事务块内的所有命令
2 exec exec命令用于执行所有事务块内的命令
3 multi multi 命令用于标记一个事务块的开始。
事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由EXEC命令原子性(atomic)地执行
4 unwatch unwatch 命令用于取消watch命令对所有key的监视
5 watch WATCH key [key ...] watch 命令用于监视一个或多个key,如果再事务执行之前这个或这些key被其他命令所改动,那么事务将被打断。

实例

下面执行一个事务的例子。它是以 multi 开始一个事务,然后将多个命令入列到事务中,最后通过exec命令来触发事务,一并执行事务中的所有命令。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set book-name 'springboot in action'
QUEUED
127.0.0.1:6379> get book-name
QUEUED
127.0.0.1:6379> sadd tag spring springboot java
QUEUED
127.0.0.1:6379> smembers tag
QUEUED
127.0.0.1:6379> exec
1) OK
2) "springboot in action"
3) (integer) 3
4) 1) "java"
2) "springboot"
3) "spring"

单个Redis命令的执行是原子性的,但是Redis没有在事务上增加任何原子性的机制,所以Redis事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量执行并不是原子性的操作,中间某条执行失败也不会导致前面的命令进行回滚,也不会导致后面的执行不会被执行。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a aaa
QUEUED
127.0.0.1:6379> set b bbb
QUEUED
127.0.0.1:6379> set c ccc
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK

说明:如果再set b bbb处失败了,set a 已经成功执行,不会进行回滚。并且set c还会继续执行。

事务中的异常情况

redis 中事务的异常情况总的来说,分为两种:

  1. 进入队列之前就能发现的错误,比如命令输错。
  2. 执行exec命令之后才能发现的错误,比如给一个非数字字符加1。

那么对于这两种不同的异常,redis中有不同的处理策略。对于第一种错误,服务器会对命令入列失败的情况进行记录,并在客户端调用exec命令时,拒绝执行并自动放弃这个事务。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3 3 3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR syntax error
4) OK
127.0.0.1:6379> keys *
1) "k4"
2) "k1"
3) "k2"

而对于第二种情况,redis并没有对它们进行特别处理,即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行。如下:

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 vv
QUEUED
127.0.0.1:6379> incr k1
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
127.0.0.1:6379> get k1
"vv"

不同于关系型数据库,redis的事务中出错时不会进行事务回滚。即redis的事务更像是命令操作的批处理。对此,官方解释如下:

Redis命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面。这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所有redis的内部可以保持简单且快速。

watch 命令

事务中的watch命令可以用来监控一个key,通过一个或多个key。通过这种监控,我们可以为redis事务提供(CAS)行为。如果有至少一个被watch监视的键在exec执行之前被修改了,那么整个事务都会被取消。exec返回nil-reply 来表示事务已经失败。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> set k4 2
OK
127.0.0.1:6379> watch k4
OK
127.0.0.1:6379> set k4 5
OK
127.0.0.1:6379> get k4
"5"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k4 8
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get k4
"5"

watch 命令可以监控一个或多个key。一旦其中一个键被修改(或删除)。之后的事务就不会执行,监控一直持续到exec命令(事务中的命令是在exec之后才执行的,exec命令执行完之后被监控的键会自动被unwatch).

通过unwatch 命令,可以取消对一个key的监控。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
127.0.0.1:6379> set kk 3
OK
127.0.0.1:6379> watch kk
OK
127.0.0.1:6379> set kk 5
OK
127.0.0.1:6379> get kk
"5"
127.0.0.1:6379> unwatch
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set kk 9
QUEUED
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379> get kk
"9"

关于Redis的事务问题就先到这了。

部分参考自:http://www.javaboy.org/2019/0615/redis-pub-sub.html

https://www.runoob.com/redis/redis-transactions.html

#

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×