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 事务 相关操作的主要内容,如果未能解决你的问题,请参考以下文章