同步操作将从 小牛肉/cs-wiki 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
严格意义来讲,Redis的事务和我们理解的传统数据库(如mysql)的事务是不一样的。
Redis 事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务的执行过程中,按照顺序执行。
Redis事务没有没有隔离级别的概念。
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
批量操作在发送 EXEC
命令前被放入队列缓存。(所有的命令在事务中,并没有直接被执行,只有发起执行命令 EXEC
的时候才会执行)
收到 EXEC
命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
(这是因为 Redis单条命令是具有原子性的,但是事务并不保证原子性)
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
🌊 一个事务从开始到执行会经历以下三个阶段:
multi
exec
127.0.0.1:6379> redis 127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
127.0.0.1:6379> redis 127.0.0.1:6379> GET book-name
QUEUED
127.0.0.1:6379> redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
127.0.0.1:6379> redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
127.0.0.1:6379> redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
🚨 事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
比如:
127.0.0.1:6379> redis 127.0.0.1:7000> 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 |
DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 |
EXEC 执行所有事务块内的命令。 |
3 |
MULTI 标记一个事务块的开始。 |
4 |
UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 |
WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
在 Redis 中使用 watch
命令可以决定事务是执行还是回滚。一般而言,可以在 multi
命令之前使用 watch
命令监控某些键值对,然后使用 multi
命令开启事务,执行各类对数据结构进行操作的命令,这个时候这些命令就会进入队列。
**当 Redis 使用 exec
命令执行事务的时候,它首先会去比对被 watch
命令所监控的键值对,如果没有发生变化,那么它会执行事务队列中的命令,提交事务;如果发生变化,那么它不会执行任何事务中的命令,而去事务回滚。**无论事务是否回滚,Redis 都会取消执行事务前的 watch
命令,这个过程如下图所示:
🚨
exec
,discard
,unwatch
命令都会清除所有监视。
Redis 参考了多线程中使用的 CAS(比较与交换,Compare And Swap)去执行的。在数据高并发环境的操作中,我们把这样的一个机制称为乐观锁(即只在数据提交的时候才去检验是否有人在此期间修改过该数据)。
事务正常运行,没有被其他线程修改:
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
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> exec # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功
1) (integer) 80
2) (integer) 20
接下来,我们新开一个命令行,在第一个命令行执行 exec
操作之前,在第二个命令行中对 money
进行修改:
显然,执行之前,另外一个线程修改了我们的值,这个时候,就会导致事务执行失败
如果修改失败,利用 unwatch
取消监视,再重新 watch
获取最新的值就可以:
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。