Redis 事务管理
Redis 提供了五个指令用于处理事务:MULTI、EXEC、DISCARD、WATCH、UNWATCH,这五个命令是 Redis 进行事务处理的基础。
这些指令允许一组命令在一个步骤中执行,其中有两个重要的保证:
一个事务中的所有命令都被序列化并按顺序执行。在 Redis 事务的执行过程中该事务服务于另一个客户端发出的请求的情况永远不会发生。这保证了被执行的命令将会被视为一个单独的隔离操作
要么处理所有命令,要么不处理任何命令,因此 Redis 事务具备原子性。
EXEC命令触发所有的命令在一个事务中执行,因此,如果客户端在调用EXEC命令之前在事务上下文中失去与服务器的连接,则不会执行任何操作,相反,如果调用EXEC命令,则执行所有操作。当使用 AOF 的数据持久化方式时,Redis将确保使用一个单独的write系统调用来将事务写入到磁盘中。然而,如果Redis服务崩溃了,或者被系统管理员以一种强硬的方式杀死,这种情况下可能会导致只保存了部分数据。Redis在重启时将检测这个情况,如果出现了错误将会退出。通过redis-check-aof工具可以修复这个问题,通过移除这一部分的事务数据使得Redis服务能够重新启动
自 Redis 2.2 版本开始,Redis 允许对上述两个保证提供额外的保证,通过乐观锁(与 CAS 的方式类似)来实现。
具体使用
MULTI、EXEC、DISCARDMULTI是Redis事务的进入点,这个命令执行之后总是返回 “OK”。执行此命令之后,用户可以输入多条命令,而不是执行这行命令,Redis会将这些命令进行排队。所有输入的命令只有在输入EXEC命令时才会被实际执行;输入DISCARD命令将会当前事务的命令队列,并且退出当前事务
可以看到,
Redis执行EXEC命令之后,会依次按照执行顺序输出执行结果WATCH、UNWATCHWATCH用于监视Key是否发生改变,如果在事务执行过程中被监测的Key被修改了,那么整个事务都将被丢弃。使用
UNWATCH可以在事务执行过程中取消对Key的监视
事务异常的处理
在一个事务的执行过程中可能会遇到两种类型的命令错误:
- 一个命令在i进入执行队列的过程中入队失败,因此在调用
EXEC命令执行这个事务之前可能会出现错误。例如,这个命令在语法上就是错误的(错误的参数个数、错误的命令名),或者一些危险的情况,如:内存溢出(如果已经将Redis服务通过maxmemory指令设置了最大内存大小) - 一个命令在调用
EXEC执行事务之后失败,例如,对具有错误值的键执行了操作(就像针对字符串值调用list操作)
客户通常会遇到第一种错误(发生在 EXEC 调用之前),对于这种情况,通过检查排队命令的返回值来进行不同的处理:如果命令以 QUEUED 回复,则表示该命令正确入队,否则 Redis 返回错误。 如果在对命令进行排队时出现错误,大多数客户端将中止当前事务并丢弃它。
从 Redis 2.6.5 开始,Redis Server 会记住在命令排队过程中发生的错误,如果出现错误则拒绝执行事务。在执行过程中也会返回错误,并且会自动丢弃事务。
如果遇到的是第二种类型的错误(发生在 EXEC 调用之后),Redis 对于这种错误不会以特殊的方式进行处理:即使在事务期间某些命令失败,所有其他命令也将被执行。
事务的回滚
注意: Redis 不支持事务的回滚操作,这点和传统的关系型数据库不同
对于此,Redis 官方有以下几点解释:
Redis命令只有在使用错误的语法调用时才会失败(并且在命令入队期间无法检测到问题),或者对支持命令操作的数据类型执行命令:这意味着实际上失败的命令是编程错误的结果,并且是一种很可能在开发过程中检测到的错误,而不是在生产中。- 由于不需要回滚的功能,因此
Redis的内部会更加简单和快速。
反对以上两个观点的一个论点是 bug 的产生,但是应该注意的是,通常回滚不会使您免于编程错误。例如,如果一个命令将键增加 2 而不是 1,或者增加操作到了错误的键,则回滚机制无法提供帮助。 鉴于没有人可以将程序员从他或她的错误中拯救出来,并且
Redis命令失败所需的错误不太可能进入生产环境,我们选择了不支持错误回滚的更简单和更快的方法。
乐观锁
WATCH 命令被用于提供给事务 CAS 的行为
被 WATCH 命令指定的 Key 将会被监视,以检测其它事务对它的修改。如果至少有一个被 WATCH 的 Key 在调用 EXEC 命令之前被修改,那么当前正在执行的整个事务都将被丢弃,并且 EXEC 命令将会返回一个 Null Reply 来提示当前的事务是执行失败的
例如,想象一下我们需要原子性地将一个 Key 的值增加 \(1\) ,按照一般的编程思想应该是首先获取 Key 的值,然后将 Key 的值加 \(1\),然后再将增加后的值放回到原来的 Key 中,伪代码如下所示:
val = GET k1
val = val + 1
SET k1 $val
这种方式只有在给定的时间段、只有一个单独的线程执行此项操作时才是可靠的,如果有多个客户端尝试通过类似的方式在相同的时间段内进行增值操作,那么这将导致 “竞态条件” 的产生。例如,客户端 A 和客户端 B 都将读取 k1 的旧值,假设旧值为 \(10\),如果这两个客户端在同一时刻读取到了旧值,并且增加了 \(1\),那么最后这两个客户端都进行 SET 操作时,最终的结果将会是 \(11\) 而不是 \(12\)
得益于 WATCH 命令的存在,使得上面的问题能够得到很好的解决:
WATCH k1
val = GET k1
val = val + 1
MULTI
SET k1 $val
EXEC
通过上面的方式,如果存在竞态条件,并且在我们执行 WATCH 命令到 EXEC 命令的执行区间中,有其它的客户端对 k1 的值进行了修改,那么我们当前的这个事务将会失败
我们只需要重新执行一次上面的代码逻辑即可(希望这次不会遇到新的竞态)。这种锁定形式被称为 “乐观锁”,是一种非常强大的锁的表现形式。在大多数情况下,多个客户端将会访问不同的 Key,因此不太可能发生碰撞(通常不需要重复操作)。
WATCH 命令的解释
WATCH 到底是什么?这是一个使得 EXEC 命令具有条件的命令:我们要求 Redis 仅在未修改任何被 WATCH 命令监视的键的情况下执行事务。这包括客户端所做的修改,如:写入命令;以及 Redis 本身所做的修改,如过期或清除。如果在事务遇到 EXEC 命令之前,被监视的 Key 被修改了,那么整个事务都将被丢弃
注意: 在 6.0.9 之前的 Redis 版本中,过期的 Key 不会导致事务中止。
WATCH 命令可以被多次调用。 简单地说,所有的 WATCH 调用都将具有监视从 WATCH 调用开始到调用 EXEC 之前的更改的效果。您还可以将任意数量的键发送到单个 WATCH 调用
当调用 EXEC 时,所有的 Key 都是 UNWATCHed(取消监视) 状态,不管事务是否被中止。 此外,当客户端连接关闭时,一切都会被取消监视。
也可以使用 UNWATCH 命令(不带参数)来取消所有被监视的键的监视。有时这很有用,因为我们乐观地锁定了一些键,因为可能我们需要执行事务来读取这些键,但是在读取键的当前内容后,我们不想继续检测这个键。
Redis 脚本和事务
Redis 脚本在定义上是具有事务性的,所以你可以用 Redis 事务做的所有事情,你也可以用脚本做,通常脚本会更简单更快。
这种重复是由于脚本是在 Redis 2.6 中引入的,而事务早已存在。 但是,我们不太可能在短期内取消对事务的支持,因为从语义上讲,即使不使用 Redis 脚本,仍然可以避免竞争条件,特别是因为 Redis 事务的实现复杂性很小。
然而,在不久的将来,我们会看到整个用户群都只是在使用脚本,这并非不可能。 如果发生这种情况,我们可能会弃用并最终事务。
参考:
[1] https://mp.weixin.qq.com/s/bs5NfSkQlFbFp7KMQ9aLmw
[2] https://redis.io/topics/transactions
Redis 事务管理的更多相关文章
- 【原】Redis事务管理
Redis高级篇 事务 MULTI, EXEC, DISCARD and WATCH命令用于保证Redis中的事务处理 一个事务中的所有命令被序列化并串行执行. 事务的原子性. 用法 MULTI ...
- Redis事务管理
用过其他关系型数据库(比如msql)的肯定都指定,在关系型数据库里面的事务可以保证多个命令操作要么同时成功,要么同时失败.并且在执行事务的时候,可以有隔离级别. 但是在Redis中的事务,只是保证事务 ...
- Redis学习笔记~Redis事务机制与Lind.DDD.Repositories.Redis事务机制的实现
回到目录 Redis本身支持事务,这就是SQL数据库有Transaction一样,而Redis的驱动也支持事务,这在ServiceStack.Redis就有所体现,它也是目前最受业界认可的Redis ...
- Redis学习——Redis事务
Redis和传统的关系型数据库一样,因为具有持久化的功能,所以也有事务的功能! 有关事务相关的概念和介绍,这里就不做介绍. 在学习Redis的事务之前,首先抛出一个面试的问题. 面试官:请问Redis ...
- Redis整理第三波(生存时间、事务管理)
expire 设置生存时间 Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即到期后数据销毁. TTL查看key的剩余时间,当返回值为-2时,表示键被删除. 当 ...
- redis的持久化 与事务管理
1. redis的持久化 Redis的持久化主要分为两部分:RDB(Redis DataBase), AOF(Append Only File) 2. 什么是redis 的持久化 在指定 ...
- Redis数据库之KEY的操作与事务管理
目的 了解并掌握各种数据类型的命令操作方式,以及各种数据类型值的操作方式.同时,主要培养对KEY的操作命令运用的能力.重点掌握对KEY信息的管理.事务常规管理和事务回滚操作. KEYS命令的练习,对K ...
- Spring Framework 中启动 Redis 事务操作
背景: 项目中遇到有一系列对Redis的操作,并需要保持事务处理. 环境: Spring version 4.1.8.RELEASE Redis Server 2.6.12 (64位) spring- ...
- spring+springmvc+mybatis+oracle+atomikos+jta实现多数据源事务管理
---恢复内容开始--- 在做项目过程中,遇到了需要一个项目中访问两个数据库的情况,发现使用常规的spring管理事务,导致事务不能正常回滚,因此,采用了jta+atomikos的分布式数据源方式 ...
- Hibernate框架笔记02_主键生成策略_一级缓存_事务管理
目录 0. 结构图 1. 持久化类的编写规则 1.1 持久化和持久化类 1.2 持久化类的编写规则 2. 主键生成策略 2.1 主键的分类 2.2 主键生成策略 3. 持久化类的三种状态[了解] 3. ...
随机推荐
- VS Code SSH
VS Code SSH 连接需要下载 VS Code Server,这是因为 VS Code Server 是在远程服务器上运行的,而不是在本地计算机上运行的.每次连接到不同的远程服务器时,都需要下载 ...
- Makefile 入门教程
Makefile 是一个非常强大的构建自动化工具,用于管理项目的编译.链接和其他构建任务.以下是一个详细的 Makefile 使用文档,包括基本概念.语法.示例和常见任务. 1. 基本概念 目标 (T ...
- PTA乙级1038C++哈希解法
#include"bits/stdc++.h" using namespace std; int main() { int a,b[105]={0}; long i,n,K; ci ...
- Java内部类与匿名类
内部类 定义: 一个类的内部又完整的嵌套了另一个类结构,被嵌套的类就被我们称为内部类,嵌套内部类的类被我们称为外部类 //外部类 class Outer { //内部类 class Inner { } ...
- P5445 [APIO2019] 路灯 题解
题目链接 题目描述 给你一个 01 串,有 \(q\) 个时刻,每个时刻要么把一位取反,要么问你在过去的所有时刻中有多少个时刻 \(a\) 和 \(b-1\) 之间都为 1. 题目分析 观察题目,我们 ...
- c#中责任链模式详解
基本介绍: "责任链"顾名思义,是指一个需要负责处理请求的链条. 每个链条节点都是一个单独的责任者,由责任者自己决定是否处理请求或交给下一个节点. 在设计模式中的解释则 ...
- 五分钟k8s实战-Istio 网关
在上一期 k8s-服务网格实战-配置 Mesh 中讲解了如何配置集群内的 Mesh 请求,Istio 同样也可以处理集群外部流量,也就是我们常见的网关. 其实和之前讲到的k8s入门到实战-使用Ingr ...
- OpenAI宫斗,尘埃落定,微软成最大赢家
周末被OpenAI董事会闹剧刷屏,ChatGPT之父Sam Altman前一天被踢出董事会,免职CEO,后一天重返OpenAI,目前结局未知. 很多同学想要围观,缺少背景知识,这里老章为大家简单介绍前 ...
- Git、GitHub、Gitlab的区别以及与SVN的比较
概念解析 Git Git是一个开源的分布式版本控制系统(Version Control System,VCS),可以有效.高速地处理项目版本管理. 版本控制是一种记录一个或若干文件内容变化,以 ...
- list.add()语句作用
----该方法用于向集合列表中添加对象 示例 本示例使用List接口的实现类ArrayList初始化一个列表对象,然后调用add方法向该列表中添加数据. public static void mai ...