Redis 提供了五个指令用于处理事务:MULTIEXECDISCARDWATCHUNWATCH,这五个命令是 Redis 进行事务处理的基础。

这些指令允许一组命令在一个步骤中执行,其中有两个重要的保证:

  • 一个事务中的所有命令都被序列化并按顺序执行。在 Redis 事务的执行过程中该事务服务于另一个客户端发出的请求的情况永远不会发生。这保证了被执行的命令将会被视为一个单独的隔离操作

  • 要么处理所有命令,要么不处理任何命令,因此 Redis 事务具备原子性。EXEC 命令触发所有的命令在一个事务中执行,因此,如果客户端在调用 EXEC 命令之前在事务上下文中失去与服务器的连接,则不会执行任何操作,相反,如果调用 EXEC 命令,则执行所有操作。当使用 AOF 的数据持久化方式时,Redis 将确保使用一个单独的 write 系统调用来将事务写入到磁盘中。然而,如果 Redis 服务崩溃了,或者被系统管理员以一种强硬的方式杀死,这种情况下可能会导致只保存了部分数据。Redis 在重启时将检测这个情况,如果出现了错误将会退出。通过 redis-check-aof 工具可以修复这个问题,通过移除这一部分的事务数据使得 Redis 服务能够重新启动

Redis 2.2 版本开始,Redis 允许对上述两个保证提供额外的保证,通过乐观锁(与 CAS 的方式类似)来实现。

具体使用

  • MULTIEXECDISCARD

    MULTIRedis 事务的进入点,这个命令执行之后总是返回 “OK”。执行此命令之后,用户可以输入多条命令,而不是执行这行命令,Redis 会将这些命令进行排队。所有输入的命令只有在输入 EXEC 命令时才会被实际执行;输入 DISCARD 命令将会当前事务的命令队列,并且退出当前事务

    可以看到,Redis 执行 EXEC 命令之后,会依次按照执行顺序输出执行结果

  • WATCHUNWATCH

    WATCH 用于监视 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 将会被监视,以检测其它事务对它的修改。如果至少有一个被 WATCHKey 在调用 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 事务管理的更多相关文章

  1. 【原】Redis事务管理

    Redis高级篇 事务 MULTI, EXEC, DISCARD and WATCH命令用于保证Redis中的事务处理 一个事务中的所有命令被序列化并串行执行. 事务的原子性. 用法 MULTI ...

  2. Redis事务管理

    用过其他关系型数据库(比如msql)的肯定都指定,在关系型数据库里面的事务可以保证多个命令操作要么同时成功,要么同时失败.并且在执行事务的时候,可以有隔离级别. 但是在Redis中的事务,只是保证事务 ...

  3. Redis学习笔记~Redis事务机制与Lind.DDD.Repositories.Redis事务机制的实现

    回到目录 Redis本身支持事务,这就是SQL数据库有Transaction一样,而Redis的驱动也支持事务,这在ServiceStack.Redis就有所体现,它也是目前最受业界认可的Redis ...

  4. Redis学习——Redis事务

    Redis和传统的关系型数据库一样,因为具有持久化的功能,所以也有事务的功能! 有关事务相关的概念和介绍,这里就不做介绍. 在学习Redis的事务之前,首先抛出一个面试的问题. 面试官:请问Redis ...

  5. Redis整理第三波(生存时间、事务管理)

    expire  设置生存时间 Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即到期后数据销毁. TTL查看key的剩余时间,当返回值为-2时,表示键被删除. 当 ...

  6. redis的持久化 与事务管理

    1. redis的持久化 Redis的持久化主要分为两部分:RDB(Redis DataBase), AOF(Append Only File) 2. 什么是redis 的持久化        在指定 ...

  7. Redis数据库之KEY的操作与事务管理

    目的 了解并掌握各种数据类型的命令操作方式,以及各种数据类型值的操作方式.同时,主要培养对KEY的操作命令运用的能力.重点掌握对KEY信息的管理.事务常规管理和事务回滚操作. KEYS命令的练习,对K ...

  8. Spring Framework 中启动 Redis 事务操作

    背景: 项目中遇到有一系列对Redis的操作,并需要保持事务处理. 环境: Spring version 4.1.8.RELEASE Redis Server 2.6.12 (64位) spring- ...

  9. spring+springmvc+mybatis+oracle+atomikos+jta实现多数据源事务管理

    ---恢复内容开始---   在做项目过程中,遇到了需要一个项目中访问两个数据库的情况,发现使用常规的spring管理事务,导致事务不能正常回滚,因此,采用了jta+atomikos的分布式数据源方式 ...

  10. Hibernate框架笔记02_主键生成策略_一级缓存_事务管理

    目录 0. 结构图 1. 持久化类的编写规则 1.1 持久化和持久化类 1.2 持久化类的编写规则 2. 主键生成策略 2.1 主键的分类 2.2 主键生成策略 3. 持久化类的三种状态[了解] 3. ...

随机推荐

  1. mysql8安装踩坑记

    背景:已安装mysql5.7版本 问题一:默认的3306端口被占用 进入mysql5.7的my.ini文件,更改port为3307或者其他未被占用的端口 问题二:Install/Remove of t ...

  2. DevOps|研发效能团队组织架构和能力建设

    研发效能团队相对于各个公司主营业务规模来说并不是很大,但是在经历的几家公司里主要是有两种组织架构,职能独立型组织架构和业务闭环型组织架构.本文主要讲解这两种组织架构的特点.优劣.劣势. 业务闭环组织架 ...

  3. Dubbo源码浅析(一)—RPC框架与Dubbo

    一.什么是RPC 1.1 RPC概念 RPC,Remote Procedure Call 即远程过程调用,与之相对的是本地服务调用,即LPC(Local Procedure Call).本地服务调用比 ...

  4. Redis系列24:Redis使用规范

    Redis系列1:深刻理解高性能Redis的本质 Redis系列2:数据持久化提高可用性 Redis系列3:高可用之主从架构 Redis系列4:高可用之Sentinel(哨兵模式) Redis系列5: ...

  5. 拟合优度R2较低怎么办

    拟合优度R2较低怎么办 (1)回归分为解释型回归和预测型回归. 预测型回归一般才会更看重2. 解释型回归更多的关注模型整体显著性以及自变量的统计显著性和经济意义显著 性即可. (2)可以对模型进行调整 ...

  6. Flask后端开发(二) - 功能实现和项目总结

    Flask后端开发(二) - 功能实现和项目总结 目录 Flask后端开发(二) - 功能实现和项目总结 1. 功能1:修改文件参数值 1.1. 获取网页端传参 1.2. 读取文件 1.2.1. 一般 ...

  7. Terraform 系列-使用Dynamic Blocks对Blocks进行迭代

    系列文章 Terraform 系列文章 Grafana 系列文章 概述 Terraform 系列文章 介绍了使用 Grafana Terraform Provider, 基于 Terraform 的 ...

  8. java——1.变量和数据类型

    变量和数据类型 字符.字节.位之间的关系 1.字符:人类可以阅读的文本内容最小单位 ​ 字符编码:utf-8,gbk 2.字节:1字符=2字节:1字符=4字节 3.位:1字节=8位 位指的是二进制位, ...

  9. JUC并发编程学习笔记(八)读写锁

    读写锁 ReadWriteLock ReadWriteLock只存在一个实现类那就是ReentrantReadWriteLock,他可以对锁实现更加细粒化的控制 读的时候可以有多个阅读器线程同时参与, ...

  10. 深度解析NLP文本摘要技术:定义、应用与PyTorch实战

    在本文中,我们深入探讨了自然语言处理中的文本摘要技术,从其定义.发展历程,到其主要任务和各种类型的技术方法.文章详细解析了抽取式.生成式摘要,并为每种方法提供了PyTorch实现代码.最后,文章总结了 ...