19 事务 相关操作

Posted 蓝风9

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了19 事务 相关操作相关的知识,希望对你有一定的参考价值。

前言

相关介绍主要围绕着 redis 里面的事务相关特性 

如下常用的命令来自于我们常见的教程 : Redis 事务 | 菜鸟教程 

本文的相关代码 拷贝自 redis-6.2.0  

代码来自于 https://redis.io/ 
 

redis 事务机制

redis 的事务机制相对来说实现的比较简单, 也和我们常见的 数据库的相关事务是存在的较大的差异 

执行方式大概是如下 : 先执行 multi 启动一个事务, 在执行 n 个 command[不会真正执行, 进入到队列], 可以执行 exec 批量执行这 n 个事务 或者 discard 丢弃这个事务 

我们这里主要是剖析如下四个场景 

1. multi + n 个正常的命令 + exec 

2. multi + m个参数正常的命令 + 1个参数不正常的命令 + n个参数正常的命令 + exec

2. multi + m个参数正常的命令 + 1个参数正常的命令但是逻辑意义不正常[xxCommand会返回错误] + n个参数正常的命令 + exec

4. multi + n 个正常的命令 + discard 

5. watch/unwatch 机制

1. multi + n 个正常的命令 + exec 

执行效果大致是如下 

127.0.0.1:6379> get name
"jerry"
127.0.0.1:6379> multi 
OK
127.0.0.1:6379(TX)> set name zzzz
QUEUED
127.0.0.1:6379(TX)> get name 
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) "zzzz"

multi 的执行

走的正常的命令的执行流程 

这里的主要业务是 更新 client 的 flags, 增加 事务标记 

set name zzzz 的执行

client 这边标记了处于 multi 执行

将 "set name zzzz" 添加到了 mstate.commands 里面, 返回给客户端 QUEUED 

get name 的执行

client 这边标记了处于 multi 执行

将 "get name" 添加到了 mstate.commands 里面, 返回给客户端 QUEUED 

exec 的执行

走的正常的命令的执行流程 

循环执行 mstate 里面的所有的命令 

2. multi + m个参数正常的命令 + 1个参数不正常的命令 + n个参数正常的命令 + exec

后面的这部分命令的执行, 我们就不会去再看 multi, 各个命令的执行, exec 的执行了 

我们主要会解释 到底执行了那些命令, 有哪些奇怪的问题, 造成的原因是什么 ? 

如下在执行第二个 set 的时候, 带了足够的参数, 但是这里的 "set name jerr1 df" 显然执行的时候是会存在问题的 

如下在执行第三个 set 的时候, 没有跟参数, 不符合参数数量的约束, redis 这里是直接把错误信息给客户端了 

但是最后执行 exec 的时候, 提示性信息是 "EXECABORT Transaction discarded because of previous errors."

127.0.0.1:6379> multi 
OK
127.0.0.1:6379(TX)> set name jerry
QUEUED
127.0.0.1:6379(TX)> set name jerr1 df
QUEUED
127.0.0.1:6379(TX)> set 
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> get name 
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.

在执行 第三个 "set" 的时候, 由于参数数量不符合期望, 走了 rejectCommandFormat 

这里面调用了 flagTransaction, 标记 事务为 dirty 

在执行 exec 的时候 

响应给客户端了 "EXECABORT Transaction discarded because of previous errors." 

并且丢弃了事务 

最终 exec 阶段是, 任何一个 事务里面 任何一个命令都没有执行 

3. multi + m个参数正常的命令 + 1个参数正常的命令但是逻辑意义不正常[xxCommand会返回错误] + n个参数正常的命令 + exec

初始的时候 name 为 zerry 

接着开启了一个事务 

第一条 set, 设置 name 为 jerry 

第二条 set, 显然是参数数量符合要求, 但是不满足 setCommand 的要求 

第三条 get, 获取 name 

最终执行结果为, 第二条 set 报错, name 为 jerry 

因此 是三条命令均执行了, 只是错误的命令不影响其他的命令的执行[参见上面 exec 的for] 

127.0.0.1:6379> set name zerry
OK
127.0.0.1:6379> get name
"zerry"
127.0.0.1:6379> multi 
OK
127.0.0.1:6379(TX)>  set name jerry 
QUEUED
127.0.0.1:6379(TX)> set name jerr1 df
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR syntax error
3) "jerry"
127.0.0.1:6379> get name
"jerry"

这个也很好解释  

multi 之后, 依次将 "set name jerry", "set name jerr1 df", "get name" 放入服务端维护的 client.mstate.commands 的队列 

然后 exec 依次执行 client.mstate.commands 里面的命令, 响应结果信息给客户端 

最终 exec 阶段是, 任何一个 事务里面 所有的一个命令都执行了 

4. multi + n 个正常的命令 + discard 

multi 开启了一个事务 

接着添加了两个命令 "set name zzzz", “get name"

然后丢弃了事务 

127.0.0.1:6379> multi 
OK
127.0.0.1:6379(TX)> set name zzzz
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> discard 
OK
127.0.0.1:6379> get name
"jerry"
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI

discar 的执行 

丢弃了事务, 并返回客户端 ok 

释放了 client.mstate 里面的保存的已经入队的命令信息 

重置了 client.mstate, client.flags 

unwatchAllKeys 

5. watch/unwatch 机制

watch 给定的 key, 如果给定的 key 在 exec 之前发生了改变, 则放弃执行 

如下执行序列, 在 exec 之前, 我再另外的一个 redis-cli 里面修改了 name 的值 

然后执行 exec, 发现 服务端返回的是 null, 怎么回事呢 ? 

127.0.0.1:6379> watch name
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379(TX)> set name zzzz
QUEUED
127.0.0.1:6379(TX)> get name
QUEUED
127.0.0.1:6379(TX)> exec 
(nil)
(102.39s)
127.0.0.1:6379> 

exec 经历了什么?

我们来看一下 exec 执行的时候, 这里 flags 为 40[CLIENT_MULTI | CLIENT_DIRTY_CAS], 因此返回的是 null 

然后之后走的时候 丢弃事务[参见上面的 discard 的命令] 的流程 

接下来, 我们来看详细的一下 这波操作 

watch name 

注册了 key -> client 到 watchedKeys "key -> [client1, client2, ... ]" 里面 

注意这里 watchedKeys 存储了两个地方, 两个地方的逻辑意义是不一样的 

client 里面存储了一份, 记录的是当前 client watch 了那些 [db, key] 

db 里面存储了一份, 存储的是当前 db 里面 key -> [client1, client2, ...] 

exec 之前, 另外一个 redis-cli 执行 set name o0o0o0

当第另外个 redis-cli 更新 name 的时候, 发送了更新通知 

更新通知这边处理的其中一件事情就是 touchWatchedKey 

对于 name 这个 key, 第一个 redis-cli watch 这个 key, 将其 flags 增加 CLIENT_DIRTY_CAS 的标记 

再回到第一个 redis-cli 的 exec 

执行 exec 的时候发现 flags 标记为 CLIENT_DIRTY_CAS 

返回给客户端 null, 并丢弃了事务 

unwatch 

前面跳到了 watchedKeys 在两个地方又维护 

client 里面存储了一份, 记录的是当前 client watch 了那些 [db, key] 

db 里面存储了一份, 存储的是当前 db 里面 key -> [client1, client2, ...] 

遍历当前 client 所维护的 watchedKeys, 从 db->watchedKeys 中移除 (key -> client), 从 client 的 watchedKeys 中移除 [db, key] 

完 

以上是关于19 事务 相关操作的主要内容,如果未能解决你的问题,请参考以下文章

JDBC事务的相关知识

Mysql相关面试题目

Spring 事务相关

2018-01-19Sql Server-视图,事务

有关jdbc以及JDBC事务的相关操作

数据库相关基础知识总结