1 Star 0 Fork 552

Leader / CS-Wiki

forked from 小牛肉 / cs-wiki 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
5-事务.md 5.49 KB
一键复制 编辑 原始数据 按行查看 历史
小牛肉 提交于 2021-07-11 21:36 . 🥽 更新目录结构

🦄 事务 Transaction


1. Redis 事务概述

严格意义来讲,Redis的事务和我们理解的传统数据库(如mysql)的事务是不一样的。

Redis 事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务的执行过程中,按照顺序执行。

Redis事务没有没有隔离级别的概念。

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

  • 批量操作在发送 EXEC 命令前被放入队列缓存。(所有的命令在事务中,并没有直接被执行,只有发起执行命令 EXEC 的时候才会执行)

  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

    (这是因为 Redis单条命令是具有原子性的,但是事务并不保证原子性)

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

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

  • 开始事务 multi
  • 命令入队
  • 执行事务 exec

2. 实例

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 还会继续执行。

3. Redis 事务命令

下表列出了 redis 事务的相关命令:

序号 命令及描述
1 DISCARD 取消事务,放弃执行事务块内的所有命令。
2 EXEC 执行所有事务块内的命令。
3 MULTI 标记一个事务块的开始。
4 UNWATCH 取消 WATCH 命令对所有 key 的监视。
5 WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

4. Redis WATCH — 监控事务

在 Redis 中使用 watch 命令可以决定事务是执行还是回滚。一般而言,可以在 multi 命令之前使用 watch 命令监控某些键值对,然后使用 multi 命令开启事务,执行各类对数据结构进行操作的命令,这个时候这些命令就会进入队列。

**当 Redis 使用 exec 命令执行事务的时候,它首先会去比对被 watch 命令所监控的键值对,如果没有发生变化,那么它会执行事务队列中的命令,提交事务;如果发生变化,那么它不会执行任何事务中的命令,而去事务回滚。**无论事务是否回滚,Redis 都会取消执行事务前的 watch 命令,这个过程如下图所示:

🚨 execdiscardunwatch 命令都会清除所有监视。

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 获取最新的值就可以:

📚 References

Java
1
https://gitee.com/mmbuy_admin/CS-Wiki.git
git@gitee.com:mmbuy_admin/CS-Wiki.git
mmbuy_admin
CS-Wiki
CS-Wiki
master

搜索帮助