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. ...
随机推荐
- 7.4 通过API枚举进程权限
GetTokenInformation 用于检索进程或线程的令牌(Token)信息.Token是一个数据结构,其包含有关进程或线程的安全上下文,代表当前用户或服务的安全标识符和权限信息.GetToke ...
- 真·Redis缓存优化—97%的优化率你见过嘛?
本文通过一封618前的R2M(公司内部缓存组件,可以认为等同于Redis)告警,由浅入深的分析了该告警的直接原因与根本原因,并根据原因提出相应的解决方法,希望能够给大家在排查类似问题时提供相应的思路. ...
- 分布式事务:XA和Seata的XA模式
上一篇内容<从2PC和容错共识算法讨论zookeeper中的Create请求>介绍了保证分布式事务提交的两阶段提交协议,而XA是针对两阶段提交提出的接口实现标准,本文则对XA进行介绍. 1 ...
- 万字长文详解Java线程池面试题
王有志,一个分享硬核 Java 技术的互金摸鱼侠加入 Java 人的提桶跑路群:共同富裕的Java人 今天是<面霸的自我修养>第 6 篇文章,我们一起来看看面试中会问到哪些关于线程池的问题 ...
- OpenTiny Vue 支持 Vue2.7 啦!
你好,我是 Kagol. 前言 上个月发布了一篇 Vue2 升级 Vue3 的文章. 少年,该升级 Vue3 了! 里面提到使用了 ElementUI 的 Vue2 项目,可以通过 TinyVue 和 ...
- 详述Java内存屏障,透彻理解volatile
一般来说内存屏障分为两层:编译器屏障和CPU屏障,前者只在编译期生效,目的是防止编译器生成乱序的内存访问指令:后者通过插入或修改特定的CPU指令,在运行时防止内存访问指令乱序执行. 下面简单说一下这两 ...
- 深度解析BERT:从理论到Pytorch实战
本文从BERT的基本概念和架构开始,详细讲解了其预训练和微调机制,并通过Python和PyTorch代码示例展示了如何在实际应用中使用这一模型.我们探讨了BERT的核心特点,包括其强大的注意力机制和与 ...
- Aspire 框架预览版发布,使云原生开发和运维更加简单
随着 .NET 8 的发布,.NET Aspire 也随之发布,这是一个全家桶框架旨在加快基于云的应用程序的构建..NET Aspire 从一开始就集成了关键组件,例如遥测和运行状况检查.它还承诺提供 ...
- 普冉PY32系列(十) 基于PY32F002A的6+1通道遥控小车I - 综述篇
目录 普冉PY32系列(一) PY32F0系列32位Cortex M0+ MCU简介 普冉PY32系列(二) Ubuntu GCC Toolchain和VSCode开发环境 普冉PY32系列(三) P ...
- 「有问必答」秒杀系统 Go并发编程实践!
有问必答 摘要 本文将介绍如何使用Go语言的并发原语来构建一个简单的高并发秒杀系统. 我们将使用Go语言的原生库和一些常见的技术手段,包括互斥锁.通道.计数器等,来解决并发访问和数据一致性的问题. 本 ...