
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉
🍎个人主页:Leo的博客
💞当前专栏: Java从入门到精通
✨特色专栏: Redis7从实战到高级
🥭本文内容:Redis事务
🖥️个人小站 :个人博客,欢迎大家访问
📚个人知识库:Leo知识库,欢迎大家访问
Redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
官网: https://redis.io/docs/manual/transactions/
我这里做了一些简单的翻译

可以一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞。
总结说:Redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
我们对数据库中事务处理的相关理论有了一个基本的认识,或许这个世界上的数据库系统千差万别,但我相信在事务处理这个问题上它们最终会殊途同归,就像我们解决并发过程中的冲突问题,常规的做法依然是加锁一样,这是我之所以要花费精力去理解和解释这些理论知识的原因,技术可谓是日新月异,如果我们总是一味地为新技术而疲于奔命,那么或许我们会渐渐地失去对这个行业的热爱,我相信原理永远比框架更为重要。
redis事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。
Redis中的事务是可以视为一个队列,即我们可以通过MULTI开始一个事务,这相当于我们声明了一个命令队列。接下来,我们向Redis中提交的每条命令,都会被排入这个命令队列。当我们输入EXEC命令时,将触发当前事务,这相当于我们从命令队列中取出命令并执行,所以Redis中一个事务从开始到执行会经历 开始事务 、 命令入队 和 执行事务 三个阶段。下面是一个在Redis中使用事务的简单示例:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET name "Leo"
QUEUED
127.0.0.1:6379> SADD Program_Language "C++" "C#" "Jave" "Python"
QUEUED
127.0.0.1:6379> GET name
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (integer) 4
3) "Leo"
我们可以注意到Redis中的事务和通常意义上的事务基本上是一致的,即
| 1.单独的隔离操作 | Redis的事务仅仅是保证事务里的操作会被连续独占的执行,redis命令执行是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的 |
|---|---|
| 2.没有隔离级别的概念 | 因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这种问题了 |
| 3.不保证原子性 | Redis的事务不保证原子性,也就是不保证所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力 |
| 4.排它性 | Redis会保证一个事务内的命令依次执行,而不会被其它命令插入 |
Redis事务和MySQL事务有以下不同点:
总的来说,Redis和MySQL事务处理有很多不同之处,它们各自适用于不同的场景。如果需要高并发的数据操作,可以使用Redis事务;如果需要对数据进行持久化存储和复杂的数据关联查询,则可以使用MySQL事务。
官网: https://redis.io/docs/manual/transactions/

MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相关的命令。



官网说明:


一个语法出错,全体连坐。如果任何一个命令语法有错,Redis会直接返回错误,所有的命令都不会执行
官网说明:

补充:


注意和传统数据库事务的区别,不一定要么全部成功要么全部失败
Redis使用Watch来提供乐观锁定,类似于CAS(Check-and-Set)
悲观锁:悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁
乐观锁:乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
乐观锁策略:提交版本必须大于记录当前版本才能执行更新
CAS

Redis使用WATCH命令来决定事务是继续执行还是回滚,那就需要在MULTI之前使用WATCH来监控某些键值对,然后使用MULTI命令来开启事务,执行对数据结构操作的各种命令,此时这些命令入队列。
当使用EXEC执行事务时,首先会比对WATCH所监控的键值对,如果没发生改变,它会执行事务队列中的命令,提交事务;如果发生变化,将不会执行事务中的任何命令,同时事务回滚。当然无论是否回滚,Redis都会取消执行事务前的WATCH命令。

在事务开始前用WATCH监控k1,之后修改k1为11,说明事务开始前k1值被改变,MULTI开始事务,修改k1值为12,k2为22,执行EXEC,发回nil,说明事务回滚;查看下k1、k2的值都没有被事务中的命令所改变。
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> WATCH k1
OK
127.0.0.1:6379> set k1 11
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 12
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> get k1
"11"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379>
watch key [key …]
初始化 k1和 k2 两个key,先监控在开启multi,保证两个key变动在同一个事务内


图中3和4不管哪个先执行,最终的结果都是整个事务执行失败

unwatch

小结
一旦执行了exec之前加的监控锁都会被取消掉
当客户端连接丢失的时候(比如退出链接),所有东西都会被取消监视
开启: 以 multi 开始一个事务
入队: 将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行: 有 exec 命令触发事务
通过上文命令执行,很显然Redis事务执行是三个阶段:
当一个客户端切换到事务状态之后, 服务器会根据这个客户端发来的不同命令执行不同的操作:
那我们如何通过一张图来判断**,服务器的判断命令是否是该入队还是该执行**。

般来说,事务有四个性质称为ACID,分别是原子性,一致性,隔离性和持久性。这是基础,但是很多文章对Redis 是否支持ACID有一些异议,我觉的有必要
梳理下:
首先通过上文知道 运行期的错误是不会回滚的,很多文章由此说Redis事务违背原子性的;而官方文档认为是遵从原子性的。
Redis官方文档给的理解是,Redis的事务是原子性的:所有的命令,要么全部执行,要么全部不执行。而不是完全成功。
redis事务可以保证命令失败的情况下得以回滚,数据能恢复到没有执行之前的样子,是保证一致性的,除非redis进程意外终结。
redis事务是严格遵守隔离性的,原因是redis是单进程单线程模式(v6.0之前),可以保证命令执行过程中不会被其他客户端命令打断。
但是,Redis不像其它结构化数据库有隔离级别这种设计。
redis事务是不保证持久性的,这是因为redis持久化策略中不管是RDB还是AOF都是异步执行的,不保证持久性是出于对性能的考虑。
本文主要参考了