Redis基础知识(二):事务机制

2023-11-04

一、什么是事务机制?

Transaction(事务)是计算机的特有术语,它一般指单个逻辑工作单位,由一系列的操作组合而成,在这些操作执行的时候,要么都执行成功,要么都不执行,防止数据结果的不一致性。
简而言之,事务是一个不可分割的工作逻辑单位。为了衡量工作单元是否具备事务能力,需要满足四个特征:ACID,即 原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

  • 原子性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的数据必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

    • 实体完整性,存在唯一的主键
    • 列完整性:字段类型、字段长度等符合所有的预设规则
    • foreign key 外键约束
    • 用户自定义完整性(如用户购物支付前后,商家收入和用户的余额总和不变)
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,会持久化到硬盘上,即便系统故障也不会丢失。

二、Redis模式下如何实现事务机制?

Redis 支持事务机制,他实现事务的关键命令包括:

MULTI、EXEC、DISCARD 、 WATCH

  • MULTI 开启事务,总是返回OK
  • EXEC 提交事务
  • DISCARD 放弃事务(即放弃提交执行)
  • WATCH 监控
  • QUEUED 命令加入执行的队列,没操作一个动作的时候,都先加入Queue

根据上述命令,Redis 事务的执行过程包含三个步骤:

  • 开启事务:MULTI
  • 命令入队:QUEUE
  • 执行事务或丢弃:EXEC 或者 DISCARD

2.1 显式开启一个事务

Client 通过 MULTI 命令显式开启一个事务,随后执行的操作将会暂时缓存在Queue中,实际并没有立即执行。

2.2 将命令入队列Queue

Client 端 把事务中的要执行的一系列操作指令发送到Service 端。 Redis服务端 实例接收到指令之后,并不是马上执行,而是暂存在命令队列中。

2.3 执行事务或丢弃

当Client端向Service端发送的命令都Ready了之后,可以发送提交执行或者丢弃事务的命令,如果是执行则操作队列中的具体指令,如果是丢弃则是清空队列命令。

  • EXEC:执行队列中的指令
  • DISCARD:丢弃保存在队列中的命令

2.4 EXEC命令执行示例

通过 MULTI 和 EXEC 执行一个事务过程:

#开启事务
> MULTI
OK
# 定义一系列指令
> set 'name' 'brand'
QUEUED
> set 'age' 18
QUEUED
> INCR 'age'
QUEUED
> GET 'name'
QUEUED
> GET 'age'
QUEUED
# 实际执行事务
> EXEC
# 获取执行结果
1) OK
2) OK
3) 19
4) "brand"
5) "19"

从上面可以看出来,每个读写指令执行后的返回结果都是 QUEUED,代表这些操作只是暂存在指令队列中,并没有实际执行。
当发送了 EXEC 命令之后,才真正执行并获取结果。

2.5 DISCARD命令:放弃事务

通过 MULTI 和 DISCARD 丢弃执行,清空指令队列:

# 初始化订数据
> SET 'name' 'brand'
OK
> SET 'age' 18
OK
# 开启事务
> MULTI
OK
# 数据增量1
> INCR 'age'
QUEUED
# 丢弃
> DISCARD
OK
# 执行结果是增量前的数据
> get 'age'
"18"

2.6 因为命令错误导致的事务回滚

体现原子性,再发生故障的时候,要么执行都成功,要么执行都失败

# 开启事务
> MULTI
OK
# 初始一个数据
> SET 'age' 18
OK
# 对该数据进行更新,但Redis不支持该命令,返回报错信息
> UPD 'age' 17
(error) ERR unknown command `UPD`, with args beginning with: `age`, `17`,
# 继续发送一个指令 ,降低age的值,该指令是正确的
> DECR 'age'
QUEUED
# 执行exec,但是之前有错误,所以Redis放弃了事务,不再执行
> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

三、Redis事务机制能实现哪些属性?

类似MySQL的事务,Redis 事务一次性可以执行多个指令, 而这多个指令通过以下的方式来保证:

  • EXEC 命令执行之前,所有的指令都是被暂存(Queued)在队列中;
  • Service端接收到EXEC命令后开始执行事务,事务中某些命令执行失败,其余命令依旧执行;
  • 在事务执行的时候具备隔离性,其他Client端执行的指令不会乱入到当前指令的执行顺序中的。

3.1 原子性(Atomicity)

在事务执行的过程中,可能遇到这几种命令执行错误:

  • 在执行 EXEC 命令前,指令本身错误:
    • 参数数量不一致构成的错误
    • 命令名称构成的错误,使用了不存在或者错误的命令:比如上面的 ‘UPD’
    • 超过MaxMemory内存限制,导致内存不足
  • 在执行 EXEC 命令后,命令的不合理操作导致的失败。比如数据类型不匹配(对 String 类型 的 value 执行了 INCR 或者 DECR 之类的操作)
  • 在执行事务的 EXEC 命令时,实例故障导致的失败,这种情况比较少一点。

3.1.1 EXEC 执行前报错

执行前错误是指命令入队(Queue)时,Redis 就会发现并记录报错。
即使执行了 EXEC命令之后,Redis也会拒绝执行指令队列中的所有指令,返回事务失败的结果。
这样一来,所有的指令都不会被执行,保持了原子性。下面是指令入队列的报错的实例,跟上面的举例一致:

# 开启事务
> MULTI
OK
# 初始一个数据
> SET 'age' 18
OK
# 对该数据进行更新,但Redis不支持该命令,返回报错信息
> UPD 'age' 17
(error) ERR unknown command `UPD`, with args beginning with: `age`, `17`,
# 继续发送一个指令 ,降低age的值,该指令是正确的
> DECR 'age'
QUEUED
# 执行exec,但是之前有错误,所以Redis放弃了事务,不再执行
> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

3.1.2 EXEC 执行后报错

这个跟上面的情况正好相反,指令入Queue时,命令的类型虽然不匹配,但是并没有在预编译的时候检查出。
只有在EXEC 命令之后,实际执行指令的时候才会报错。其他正确的指令还是会执行成功,不保证原子性。 参考下面:

# 开启事务
> MULTI
OK
> set age 18
QUEUED
> set name 'brand'
QUEUED
> INCR age
QUEUED
# 这边对String类型进行DECR,没有报错,但是在执行指令的时候会报错误
> DECR name
QUEUED
# 执行,会发现其他三条执行执行成功,只有一条执行失败,返回报错信息
> EXEC
1) OK
2) OK
3) 19
4) ERR value is not an integer or out of range
# 查看结果
> get name
"brand"
> get age
"19"

3.1.3 在EXEC执行时发生实例故障

可以使用AOF日志,把未完成的事务操作从AOF日志中去除,之后使用AOF进行恢复时就不会被再次执行,以此保证整个操作的原子性。
这个需要Redis启用AOF日志这个持久化能力。

3.1.4 对于上述几种错误特征的总结

  • 指令入队列时有报错(所有指令中只要有一条不是QUEUED),就会放弃事务执行,保证原子性。如 3.1.1
  • 指令入队列时没报错(所有指令都是QUEUED),但在实际执行EXEC时报错,则不保证原子性。如 3.1.2
  • EXEC执行时出现故障,如果开启了 AOF 日志,可以保证原子性。如 3.1.3

3.2 一致性

跟原子性类似,一致性会受到错误指令、执行异常、Redis故障等情况的影响,主要有如下几种情况:

  • 指令入队列时有报错,事务被放弃执行,所以可以保证一致性。
  • 指令入队列时正产,实际执行EXEC时报错,则是错误部分不会执行,正确指令依旧正常执行,也可以保证一致性。
  • Redis实例故障分成几种:
    • 未开启持久化情况,故障重启后数据都清空,结果是一致的。
    • RDB快照:事务命令操作的结果不被保存到 RDB 快照中,所以在恢复时,数据结果是一致的。
    • AOF 日志:发生故障时,使用 redis-check-aof 清除事务中对应操作,数据库恢复后也保持一致。

3.3 隔离性

从隔离性这个角度,事务执行的时机可以分成两种:

  • 一种是操作在EXEC执行之前(纯入队期间),这时候采用 WATCH 的机制来保障
  • 另一种是开始执行EXEC之后(实际开始执行命令了),这时候本身具备隔离性了。

3.3.1 WATCH监测对象是否有变化

如果前后有变化,说明被修改了,这时就放弃事务执行,避免事务的隔离性被破坏。
image

3.3.2 对操作进行顺序,并发操作排在 EXEC 之后

Redis 操作命令是单线程执行的,所以在EXEC 命令执行后,不会乱入其他操作,Redis 会保证把指令队列中的所有指令都操作完成之后。
在执行后续的命令,所以,这种模式并发操作不会破坏事务的隔离性。它具有天然的隔离能力。
image

3.4 持久性

因为Redis的持久化特性,所以有如下三种可能性:

  • 未开启 RDB快照 或 AOF日志,事务肯定不具备持久化能力。
  • RDB快照模式:我们在Redis持久化那一篇中聊过,RDB具有快照间隙,事务执行在快照之间则不会被保障。
  • AOF日志:无论日志持久化选项是 no、everysec 和 always 都会存在数据丢失的情况,所以也是无法完全保障的。
    所以不管 Redis 采用什么持久化模式,事务的持久性属性是得不到完全保证的。

四、总结

  • Redis 具备了一定的原子性,但不支持回滚。DISCARD 主要负责清空指令列表,放弃操作。
  • Redis 具备一致性的能力
  • Redis 具备隔离性的能力
  • Redis 无法保证持久性
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis基础知识(二):事务机制 的相关文章

  • 如何在多线程应用程序中使用 StackExchange.Redis IDatabase 对象?

    我从 StackExchange Redis 文档中收到有关如何使用 IDatabase 的混合消息 在里面基本使用文档 https github com StackExchange StackExchange Redis blob mas
  • 有没有办法在 Redis 和关系数据库中使用带有 @RedisHash 的实体?

    我正在使用Spring引导 为了将我的实体保存在关系数据库上 我配置了一个数据源和我的域类 例如 Entity Table schema schema name name tb name public class table name ex
  • 是否可以使用带有 FUSE 文件系统的 Linux VFS 缓存?

    默认情况下 Linux VFS 缓存似乎不适用于 FUSE 文件系统 例如 read 调用似乎被系统地转发到 FUSE 文件系统 我在 FUSE 特定的远程文件系统上工作 我需要一个非常积极的缓存 我需要实现自己的页面缓存吗 或者是否可以为
  • Redis多插入问题

    我尝试多次插入 但它给了我错误 http pastie org 7337421 http pastie org 7337421 cat mass insert txt 3 r n 3 r nSET r n 3 r nkey r n 5 r
  • Redis 写入 .ssh/authorized_keys

    当前设置 2 个主服务器 12 个工作服务器 工作人员通过 ssh copy id 连接到主设备 主设备和工作人员正在主设备上的 redis 队列中写入数据 过去一周我遇到的问题是 Redis 正在将数据写入authorized keys
  • 找不到模块“socket.io/node_modules/redis”

    当尝试做的时候 var redis require socket io node modules redis 我收到错误 找不到模块 socket io node modules redis 我不明白为什么 我正在运行 Windows 并运
  • 使用 sidekiq 处理两个独立的 Redis 实例?

    下午好 我有两个独立但相关的应用程序 他们都应该有自己的后台队列 阅读 单独的 Sidekiq 和 Redis 进程 然而 我希望偶尔能够将工作推给app2的队列来自app1 从简单的队列 推送的角度来看 如果app1没有现有的 Sidek
  • PooledRedisClientManager 未释放连接

    我将 json 数据列表存储在 redis 中并使用 ServiceStack c 客户端访问它 我本质上是在管理自己的外键 我在其中存储zrangeid 我使用应用程序内部的接口从zrange然后从 Redis 获取底层 json 对象并
  • 如何让客户端下载动态生成的非常大的文件

    我有一个导出功能 可以读取整个数据库并创建一个包含所有记录的 xls 文件 然后文件被发送到客户端 当然 导出完整数据库的时间需要大量时间 并且请求很快就会以超时错误结束 处理这种情况的最佳解决方案是什么 例如 我听说过使用 Redis 创
  • WSL Redis 遇到系统尚未使用 systemd 作为 init 系统(PID 1)启动。无法操作[已关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我正在尝试遵循本文中讨论的 Redis 安装过程article https www digitalocean com community
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • Spring Data Redis - Lettuce连接池设置

    尝试在 spring data redis 环境中设置 Lettuce 连接池 下面是代码 Bean LettuceConnectionFactory redisConnectionFactory GenericObjectPoolConf
  • 如何测试我的 Redis 缓存是否正常工作?

    我已经安装了 django redis cache 和 redis py 我遵循了 Django 的缓存文档 据我所知 以下设置就是我所需要的 但我如何判断它是否正常工作 设置 py CACHES default BACKEND redis
  • Redis发布/订阅:查看当前订阅了哪些频道

    我目前有兴趣查看我拥有的 Redis 发布 订阅应用程序中订阅了哪些频道 当客户端连接到我们的服务器时 我们将它们注册到如下所示的通道 user user id 这样做的原因是我希望能够看到谁 在线 目前 我在不知道客户端是否在线的情况下盲
  • Redis Cluster 与 Pub/Sub 中的 ZeroMQ,用于水平扩展的分布式系统

    如果我要设计一个巨大的分布式系统 其吞吐量应随系统中的订阅者数量和通道数量线性扩展 哪个会更好 1 Redis集群 仅适用于Redis 3 0 alpha 如果是集群模式 您可以在一个节点上发布并在另一个完全不同的节点上订阅 消息将传播并到
  • 为什么 Redis TimeSeries 不捕获聚合中的最后一个元素?

    我试图了解 Redis 的时间序列规则创建的工作原理 但我很困惑为什么 Redis 会忽略聚合中的最后一项 并想知道这是否是预期的行为 我在中创建了示例代码redis cli为了显示 127 0 0 1 6379 gt FLUSHALL O
  • Java 将字节转换为二进制安全字符串

    我有一些以字节为单位的数据 我想将它们放入Redis中 但是Redis只接受二进制安全字符串 而我的数据有一些二进制非安全字节 那么如何将这些字节转换为二进制安全字符串以便将它们保存到 Redis 中呢 Base64 对我有用 但它使数据更
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • Redis是如何实现高吞吐量和高性能的?

    我知道这是一个非常普遍的问题 但是 我想了解允许 Redis 或 MemCached Cassandra 等缓存 以惊人的性能极限工作的主要架构决策是什么 如何维持连接 连接是 TCP 还是 HTTP 我知道它完全是用C写的 内存是如何管理
  • 在 Redis 上为 Django 和 Express.js 应用程序共享会话存储

    我想创建一个包含一些登录用户的 Django 应用程序 另一方面 由于我想要一些实时功能 所以我想使用 Express js 应用程序 现在的问题是 我不希望身份不明的用户访问 Express js 应用程序的日期 因此 我必须在 Expr

随机推荐

  • 循环点击链接selenium模拟

    https blog csdn net qq 43251443 article details 82819887转载
  • 私有地址和保留地址

    A类 10 X X X是私有地址 私有地址就是在互联网上不使用 而被用在局域网络中的地址 127 X X X是保留地址 用做循环测试用的 B类 172 16 0 0 172 31 255 255是私有地址 169 254 X X是保留地址
  • 深度学习进阶线路图

    研究动态 深度学习进阶线路图 一 在应用机器学习的时候 最耗时和重要的阶段是对原始数据进行特征提取 深度学习是一个新的机器学习的分支 他要做的就是跨过整个特征设计阶段 而是直接从数据中学习得到 大部分的深度学习方法都是基于神经网络的 在这些
  • Git命令大全

    Git命令大全 一 git config 二 git clone 三 git init 四 git status 五 git remote 六 git branch 七 git checkout 八 git add 九 git commit
  • 区块链技术对金融行业有什么冲击?

    区块链技术在经过了长达十年的发展 被越来越多的行业关注 特别是一些大型企业 对区块链技术还进行了深入的研究 区块链技术也在更多的领域被应用 区块链技术的热度虽然很高 但目前的发展还处在初级阶段 其过多的应用场景也是没有得到更大的发展 区块链
  • 蓝桥杯练习系统入门水题

    好几天没写代码了 上蓝桥杯的练习系统看了一下 做了四道巨水题之后发现有些题还要vip 无语 问题描述 Fibonacci数列的递推公式为 Fn Fn 1 Fn 2 其中F1 F2 1 当n比较大时 F
  • (计算机组成原理)指令的寻址方式

    指令寻址方式是指指令或者操作数有效地址的寻找方式 主要分为数据寻址和指令寻址 指令的地址码字段往往并不是操作数的真实地址 而是形式地址 用A表示 A 即操作数形式地址所指向的存储介质的数值 用形式地址结合指令的寻址方式可以计算出操作数的真实
  • 快速调整毕业论文格式:调整参考文献的引用样式和段落格式

    在撰写毕业论文的过程 我们需要参考并引用大量的参考文献 之前有介绍了如何在Word中使用Endnote插入参考文献 但是从Endnote样式网站下载的样式可能和学校要求的参考文献的引用格式和段落样式有些出入 我们需要根据需求在下载样式上进行
  • 二叉树 Binary Tree

    二叉树 二叉树的基本概念 1 什么是二叉树 2 二叉树的优点和缺点 3 二叉树的基本名词 4 二叉树的性质 5 特别的二叉树 满二叉树 Full Binary Tree 完全二叉树 Complete Binary Tree 平衡二叉树 Ba
  • 【C++】多态

    文章目录 1 多态的基本概念 2 动态联编和静态联编 2 多态的原理剖析 3 计算器案例 4 抽象类与纯虚函数 5 虚析构和纯虚析构函数 6 向上类型转换和向下类型转换 1 多态的基本概念 多态性提法接口和具体实现之间的另一层隔离 多态改善
  • 计算机硬件结构简略介绍

    前言 计算机硬件结构简略介绍 一 计算机硬件 从软件开发者的角度来看 计算机硬件有三个部件最为关键 分别是中央处理器CPU 内存 I O控制芯片 二 早期 早期计算机 CPU的核心频率不高 和内存的频率一样 他们都是直接连接在同一个总线 b
  • 面试之设计模式(简单工厂模式)

    案例 在面试时 面试官让你通过面对对象语言 用Java实现计算器控制台程序 要求输入两个数和运算符号 得出结果 大家可能想到是如下 public static void main String args Scanner scanner ne
  • GDB print 详解

    GDB print 详解 分类 Linux GDB 2013 04 08 11 07 145人阅读 评论 0 收藏 举报 Linux GDB 察看变量 目录 print命令的格式是 print xxx p xxx 1 print 操作符 是
  • 请说出三种减少页面加载时间的方法

    1 尽量减少页面中重复的HTTP请求数量 2 服务器开启gzip压缩 3 css样式的定义放置在文件头部 4 Javascript脚本放在文件末尾 5 压缩合并Javascript CSS代码 6 使用多域名负载网页内的多个文件 图片
  • 在Windows 11 中安装和使用 WSL 2

    文章目录 列出可安装的发行版 分发 安装WSL 2 常用命令 显示帮助 启动分发 从powershell中退出分发 关闭正在运行的分发 立即终止所有正在运行的分发和 WSL 2轻型虚拟机 更新wsl 使用VSCode连接WSL 设置代理 换
  • QEMU-从buildroot里面编译kernel(7)

    上面是我的微信和QQ群 欢迎新朋友的加入 下载交叉编译工具 https snapshots linaro org gnu toolchain 选一个最新的 选择压缩包 解压 sudo apt get install g sudo mv gc
  • 网狐棋牌:数据库

    jeefwtwo 账号数据库 QPAccountsDB 账号账号数据库 QPGamescoreDB 游戏积分数据库 QPGameMatchDB 比赛数据库 QPPlatformDB 平台数据库 QPRecordDB 记录数据库 QPTrea
  • vue-video-player基本使用

    下载 npm install vue video player 如果不使用vue的话 可以直接去官网 或者cdn获取对应js即可 在vue中的基本使用 main js中 全局 import Vue from vue import VueVi
  • 线程与进程的对比、互斥锁和条件变量的使用-多线程编程

    线程与进程的对比 线程的概念是共享CPU的需要 进程概念是共享内存的需要 一个进程里代码段 数据段 堆是共享的 但是进程中的每个线程中的栈 寄存器内容独立 进程都是独立的 通常的IPC 管道 共享内存都可以通讯 处于一个线程的代码觉得它拥有
  • Redis基础知识(二):事务机制

    文章目录 一 什么是事务机制 二 Redis模式下如何实现事务机制 2 1 显式开启一个事务 2 2 将命令入队列Queue 2 3 执行事务或丢弃 2 4 EXEC命令执行示例 2 5 DISCARD命令 放弃事务 2 6 因为命令错误导