Redis 事务说明与 watch 命令监控事务

Redis 的事务说明

官网链接 Transactions – Redis

redis 中的事务可以理解为:一组命令的集合。在一个事务中的所有命令都会序列化、按顺序地执行。

事务操作相关命令

事务操作流程

  • 开启事务       multi
  • 提交事务       exec
  • 回滚事务       discard
命令 说明
discard 取消事务,放弃执行事务块内的所有命令
exec 执行所有事务块内的命令
multi 开启事务,标记一个事务的开始
watch 监视一个key 或者 多个 key,如果在事务执行之前,key 的值被其他命令改动,那么事务将被打断(事务会被放弃执行)
unwatch 取消观察事务

详细说明

正常执行事务

当 multi 开启事务之后,所有的命令操作都会进入一个执行队列,可以从命令返回信息(QUEUED)上面看到,当 执行 exec 命令的时候,所有的命令操作就会按照顺序执行。

127.0.0.1:6379> multi                # 开启事务
OK
127.0.0.1:6379> set name 11111       # 事务相关操作
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> lpush list v1 v2
QUEUED
127.0.0.1:6379> exec                 # 提交事务
1) OK
2) "11111"
3) OK
4) (integer) 2
127.0.0.1:6379> lrange list 0 -1     # 查看事务中的操作
1) "v2"
2) "v1"
127.0.0.1:6379>

放弃事务

这里测试先开启事务,进行一些命令操作,然后放弃事务,这个时候事务已经被回滚了,如果我们执行获取事务设置的 key 是获取不到的,并且如果提交事务的话,会报错(因为事务被放弃)。

127.0.0.1:6379> multi                    # 开启事务
OK
127.0.0.1:6379> set name Tom             # 进行事务操作
QUEUED
127.0.0.1:6379> set age 40
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> discard                  # 放弃事务
OK
127.0.0.1:6379> get age                  # 放弃事务之后,继续读取之前设置的 key
(nil)
127.0.0.1:6379> exec                     # 测试提交事务,这里会报错,因为没有开启事务
(error) ERR EXEC without MULTI
127.0.0.1:6379>

编译型异常

是指在开启事务之后,输入的命令报错(就是命令的语法有误,或者是不存在的命令):所有的操作都不会提交。

127.0.0.1:6379> multi                # 开启事务
OK
127.0.0.1:6379> set name tx          # 设置指
QUEUED
127.0.0.1:6379> get name tx          # 执行错误的命令,会显示报错信息
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> set book java        # 继续执行后续操作
QUEUED
127.0.0.1:6379> get book
QUEUED
127.0.0.1:6379> exec                 # 提交事务,会显示报错信息,事务被取消,前面的命令操作不会被提交
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>

运行时异常

就是指执行的命令语法是正确的,但是语法执行的操作是会引起报错:报错的那行命令不会被执行,报错命令行后面的操作会继续执行。

127.0.0.1:6379> multi                # 开启事务操作
OK
127.0.0.1:6379> set name Tim
QUEUED
127.0.0.1:6379> set age 30
QUEUED
127.0.0.1:6379> incr name            # 这里会报错,因为字符串是不能被进行 incr 增加操作的
QUEUED
127.0.0.1:6379> set book Java        # 这里是测试前面报错之后,会不会继续执行后续的操作
QUEUED
127.0.0.1:6379> get book
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec                 # 提交事务
1) OK
2) OK
3) (error) ERR value is not an integer or out of range        # 这里可以看到虽然是命令报错了,但是依旧是正常执行成功
4) OK
5) "Java"
6) "Tim"
127.0.0.1:6379> get name             # 查看事务中的命令操作 
"Tim"
127.0.0.1:6379>

watch 与 unwatch 的使用

在 redis 中使用 watch 命令可以决定事务是执行还是回滚。一般而言,可以在 multi 命令之间使用 watch 命令监控某些键值对,然后使用 multi 命令开启事务。

这里需要注意的点是:watch 不能在事务里面使用(但是并不影响后续的命令执行)。这里有一个特殊的现象,在事务中执行 watch 命令,虽然会有报错提示(类似于编译时异常),但是后续的命令操作会继续执行,并不会回滚事务,但是如果是在事务中存在有其他的命令报错(编译时异常),那么事务是会回滚的,具体看下面的代码:

# watch 在事务中使用的情况:执行 watch 时会报错,但是不影响后续命令执行
127.0.0.1:6379> set name Tim
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> watch name
(error) ERR WATCH inside MULTI is not allowed
127.0.0.1:6379> set name Tom
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec
1) OK
2) "Tom"
127.0.0.1:6379> get name
"Tom"
127.0.0.1:6379>


# 在事务中执行 watch,并且出现有其他的语法错误,命令是不会执行的
127.0.0.1:6379> set name Tom
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> watch name
(error) ERR WATCH inside MULTI is not allowed
127.0.0.1:6379> get name tom
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> get age
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>

正常的 watch 操作

这里需要开启两个窗口来做测试,在第一个窗口没有提交事务之间,在第二窗口中修改第一个窗口的值。

#  第一个窗口
127.0.0.1:6379> set name Tom            #  设置值
OK
127.0.0.1:6379> watch name              #  对值进行监控
OK
127.0.0.1:6379> multi                   #  开启事务
OK
127.0.0.1:6379> set name Jim            #  设置值
QUEUED
127.0.0.1:6379> get name                #  这里需要注意,在另一个窗口中执行操作
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> exec                    #  提交事务,这个时候事务被回滚
(nil)
127.0.0.1:6379> get name
"Tim"
127.0.0.1:6379>




#  第二个窗口
127.0.0.1:6379> get name                # 获取 name 值
"Tom"
127.0.0.1:6379> set name Tim            # 在第一个窗口没有 exec 之前执行
OK
127.0.0.1:6379> get name
Tim
127.0.0.1:6379>

模拟事务操作

这里也需要使用两个窗口,每个窗口都是进行事务操作,在第一个窗口中执行 exec 提交事务之前,在第二个窗口中修改第一个窗口的数据,然后第一个窗口提交,第二个窗口再提交。

# 第一个窗口操作
127.0.0.1:6379> set money 100        #  设置值
OK
127.0.0.1:6379> set out 10
OK
127.0.0.1:6379> watch money          #  监控 money
OK
127.0.0.1:6379> multi                #  开启事务
OK
127.0.0.1:6379> decrby money 20      #  进行数据修改
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> get money            #  查看数据
QUEUED
127.0.0.1:6379> get out              #  此时,去第二个窗口中操作
QUEUED
127.0.0.1:6379> exec                 #  在第二个窗口中执行 exec 之前执行提交
1) (integer) 80
2) (integer) 30
3) "80"
4) "30"
127.0.0.1:6379>



# 第二个窗口操作
127.0.0.1:6379> watch money           # 观察值 
OK
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> multi                 # 开启事务
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec                  # 在第一个窗口中执行 exec 之后执行,这个时候会发现返回为 nil 表示事务失败
(nil)
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> get out
"30"
127.0.0.1:6379>

unwatch 操作

是取消 watch 命令对所有 key 的监视。

如果在执行 watch 命令之后,exec 命令 或 discard 命令先被执行了的话,那么就不需要再执行 unwatch 了。

因为 exec 命令会执行事务,因此 watch 命令的效果已经产生,而 discard 命令在取消事务的同时,也会取消所有对 key 的监视。

猜你喜欢

转载自blog.csdn.net/qq_18948359/article/details/115329943
今日推荐