Redis系列:Redis 的事务机制

news2024/9/23 13:26:37

1 复习下何为事务机制?

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

  • 原子性(Atomicity):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的数据必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
    • 实体完整性,存在唯一的主键
    • 列完整性:字段类型、字段长度等符合所有的预设规则
    • foreign key 外键约束
    • 用户自定义完整性(如用户购物支付前后,商家收入和用户的余额总和不变)
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,会持久化到硬盘上,即便系统故障也不会丢失。

2 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.

3 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 采用什么持久化模式,事务的持久性属性是得不到完全保证的。 

4 总结

  • Redis 具备了一定的原子性,但不支持回滚。DISCARD 主要负责清空指令列表,放弃操作。
  • Redis 具备一致性的能力
  • Redis 具备隔离性的能力
  • Redis 无法保证持久性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/806968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ENSP中路由器配置详解

目录 一:两个主机,两个路由器配置连接 二:两主机,三路由器配置连接 三:配置浮动路由 一:两个主机,两个路由器配置连接 打开ENSP 1.点击新建拓扑 2.点击路由器 3.将两个AR3260路由器拖出来…

ShardingSphere-Proxy读写分离详解与实战

🚀 ShardingSphere 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜&…

基于springboot的公交线路查询系统vue

本次开发一套公交线路查询系统有管理员,用户两个角色。管理员功能有个人中心,用户管理,公交线路管理,网站公告管理,友情链接管理,留言管理,系统管理。用户可以注册登录,查看公告线路…

java使用easyexcel读写文件

文章目录 前言一、引入pom二、write1.代码部分2.执行结果 三、read1.代码部分2.运行结果 总结 前言 Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题…

掌握文件锁:使用flock实现多个进程之间的无缝文件同步

使用flock实现多个进程之间的无缝文件同步? 博主简介一、引言二、文件锁的概述2.1、定义文件锁2.2、文件锁的种类2.3、文件锁的作用 三、使用flock实现文件锁3.1、flock的简介3.2、flock的使用方法3.3、flock文件锁命令3.4、flock对文件同步的帮助 四、实现多个进程…

Sestra 实用教程(二)方程求解器

目 录 一、前言二、超单元分析三、惯性释放四、模态叠加法4.1 Eigenvalue solvers4.2 Static back substitution 五、模态综合法六、Master-Slave七、参考文献 一、前言 SESAM (Super Element Structure Analysis Module)是由挪威船级社(DNV-…

QMediaPlayer播放视频

简介 使用QT的QMediaPlayer类播放MP4格式的视频。 代码示例 头文件 Ui::MainWindow *ui; QMediaPlaylist *playlist; QMediaPlayer *player; QVideoWidget *videoWidget;方式一 应用窗体外播放视频。 .cpp文件 playlist new QMediaPlaylist;playlist->addMedia(QUrl(…

ShardingSphere-Proxy垂直分片详解与实战

🚀 ShardingSphere 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜&…

python:图片修改尺寸和裁剪

作者:CSDN @ _养乐多_ 本文记录了使用python语言的PIL库对jpg格式的图像进行修改尺寸,按指定像素间隔裁剪的代码。 文章目录 一、修改图片尺寸二、按照指定像素裁剪图片2.1 将小块保存在本地2.2 将小块以一定间隔生成新的图片原图: 一、修改图片尺寸 修改尺寸后, from PI…

c++学习(布隆过滤器)[23]

布隆 布隆过滤器(Bloom Filter)是一种概率型数据结构,用于判断一个元素是否可能存在于一个集合中。它使用多个哈希函数和位图来表示集合中的元素。 布隆过滤器的基本原理如下: 初始化:创建一个长度为m的位图&#xf…

ARM 详解

一、ARM 的发展史 ARM 的发展历史可以追溯到 1978 年,当年克里斯库里(Chris Currry)所任职的公司遭遇财务危机,发展每况愈下,库里在和创始人深入沟通后,决定离职。当时的库里对微型计算机很感兴趣&#xf…

大致了解Redis

为了保证数据的可靠性,Redis 需要在磁盘上读写 AOF 和 RDB,但在高并发场景里,这就会直接带来两个新问题:一个是写 AOF 和RDB 会造成 Redis 性能抖动,另一个是 Redis 集群数据同步和实例恢复时,读 RDB 比较慢…

什么是EPC?

文章目录 一、什么是EPC?二、EPC的优势和特征2.1、EPC的优势2.2、EPC的特征 三、EPC项目管理平台3.1、平台简介3.2、平台优势3.3、平台功能模块 一、什么是EPC? EPC(Engineering Procurement Construction)是指公司受业主委托&am…

MySQL中锁的简介——表级锁-元数据锁、意向锁

1.元数据锁 查看元数据锁 select object_type,object_scheme,object_name,lock_type,lock_duration from perfomance_scheme.metadata_locks;2.意向锁 线程A开启事务后在执行update更新语句时候,会给数据加上行锁,加上行锁以后,会对整张表加…

JMM是如何保证原子性呢?

3.3.1加锁 锁是一种通用技术,比如Java提供的Synchronized关键字就是锁的一种实现,Synchronized是排他锁/独占锁,就是有你没我的意思,只要其他线程到来访问,发现锁还未释放,就要在外面等待,因为S…

【雕爷学编程】Arduino动手做(175)---机智云ESP8266开发板模块6

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

Docker 入门终极指南[详细]

前言 富 Web 时代,应用变得越来越强大,与此同时也越来越复杂。集群部署、隔离环境、灰度发布以及动态扩容缺一不可,而容器化则成为中间的必要桥梁。 本节我们就来探索一下 Docker 的神秘世界,从零到一掌握 Docker 的基本原理与实…

Nginx配置WebSocket反向代理

1、WebSocket协议 ​ WebSocket协议相比较于HTTP协议成功握手后可以多次进行通讯,直到连接被关闭。但是WebSocket中的握手和HTTP中的握手兼容,它使用HTTP中的Upgrade协议头将连接从HTTP升级到WebSocket。这使得WebSocket程序可以更容易的使用现已存在的…

Java8使用CompletableFuture实现多异步任务同步等待结果

一、应用场景 在实际的开发工作中,我们可能会遇到一些比较复杂且耗时的功能,例如一个业务数据表格的导出,假定业务数据表本身都很大,需要组装的数据又比较多,就会造成整个实现过程很耗时甚至可能出现接口请求超时&…

Android 之 MediaPlayer 播放音频与视频

本节引言: 本节带来的是Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码 和播放音视频。它支持三种不同的…