Redis的事务机制能保证ACID属性吗?

server/2024/9/20 1:20:17/ 标签: redis, 数据库

目录

事务 ACID 属性

用户如何开启Redis的事务?

redis-cli%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%9D%A5%E5%B1%95%E7%A4%BA-toc" style="margin-left:40px;">使用redis-cli客户端来展示

​Go语言编码使用事务

redis-的事务机制能保证哪些属性-toc" style="margin-left:0px;">Redis 的事务机制能保证哪些属性?

1. 原子性

语法错误

运行错误

执行EXEC时,Redis发生故障

Redis对事务原子性属性的保证情况

 2. 一致性

命令入队就错误

命令在实际执行时才报错

EXEC命令执行时,Redis发生故障

3. 隔离性 

4. 持久性

总结


为什么要把这批操作 称为 事务呢?是因为一批操作在执行过程中想获得一些保证。而事务就提供了这些保证。

事务在执行的时候,会提供专门的属性保护,包括原子性、一致性、隔离性和持久性。

事务 ACID 属性

原子性(Atomicity)

事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的。业务应用使用事务时,原子性也是最被看重的一个属性。

一致性(Consistency)
一个事务必须使数据库从一个一致性状态变换到另一个一致性状态,是指数据库中的数据在事务执行前后是一致的。

比如说转账, 用户A余额200元,用户B余额100元,A给B转账100元,事务结束后,用于A余额是100元,用户B余额是200元,这个就是一致性。如果A上的钱减少了,而B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。

隔离性(Isolation)
一个事务的执行不能被其他事务干扰。它要求数据库在执行一个事务时,其它操作无法存取到正在执行事务访问的数据。

比如某件商品库存10件,在一个事务过程中,用户A对该商品下单6件,而另外,用户B也对该商品下单5件,那累加后就超过库存数了,不符合业务要求。所以是,在事务的修改某数据时候,其他的不能修改该数据,但是可以访问该数据,但只能访问到事务开始前的值。这样就是两个进行了隔离。

持久性(Durability)
一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。当事务提交之后,数据会持久化到硬盘,修改是永久性的。

用户如何开启Redis的事务?

事务的执行过程包含3个步骤,Redis提供了MULTI、EXEC两个命令来完成这三个步骤。

  1. 客户端使用 MULTI 命令显式表示开启一个事务
  2. 客户端吧事务中要执行的操作(比如增删改数据)发送给服务端。注意:这些命令虽然被客户端发送到了服务端,但Redis实例只是把这些命令暂存到一个命令队列中,并不会立即执行。
  3. 客户端向服务器发送提交事务的命令 EXEC,让数据库实际执行第二步中发送的具体操作。当服务端收到该命令后,才会真正执行命令队列中的所有命令。

 

使用redis-cli客户端来展示

事务过程中它们执行后的返回结果都是 QUEUED,这就表示,这些操作都被暂存到了命令队列,还没有实际执行。等到执行了 EXEC 命令后,可以看到返回了结果,这就表明,事务中的操作已经成功地执行了。

Go语言编码使用事务

func main() {client := redis.NewClient(&redis.Options{Addr:     "127.0.0.0.1:6379",Password: "", //若是设置了密码,就需要填写DB:       0,})ping, _ := client.Ping().Result()    //测试是否网络通fmt.Println(ping)//该方法注解//TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.pipe := client.TxPipeline()val, _ := pipe.Set("key1", 100, 0).Result()fmt.Println("set key1 ", val) //该结果要在执行exec后才会有pipe.Set("key2", "200", 0)pipe.Get("key2")//执行命令队列中的命令_, err := pipe.Exec()if err != nil {fmt.Println(err)return}fmt.Println("执行exec 后 set key1 ", val)value1, err := client.Get("key1").Result()if err != nil {fmt.Println(err)return}fmt.Println("key1: ", value1)
}

redis-的事务机制能保证哪些属性">Redis 的事务机制能保证哪些属性?

1. 原子性

要求是批操作是全部成功或者全部失败。要是在事务过程中,出现了错误,Redis还能保证原子性吗?这个要分三种情况来分析。

语法错误

语法错误指的是命令的参数个数不正确,命令本身拼写错误等情况。

在执行EXEC命令时,客户端发送的操作命令本身就有错误(比如使用了不存在的命令),在命令入队时候就被Redis判断出来。

在命令入队时候,Redis就会报错并记录下该错误。而此时,我们还能继续提交命令。等到执行EXEC之后,Redis就会拒绝执行所有提交的命令操作,返回错误的结果。

这样,事务中的所有命令都不被执行,保证了原子性。

运行错误

运行错误是指输入的命令格式正确,但是在命令执行期间出现了错误。典型的及时命令和操作的数据类型不服务的情况。比如添加一个string类型的key,之后却对该key进行列表操作。

127.0.0.1:6379> set skey ss
OK
127.0.0.1:6379> lpush skey value
(error) WRONGTYPE Operation against a key holding the wrong kind of value

那么,事务操作入队时候,命令和操作的数据类型不匹配,但是Redis没有检查出错误。但是在执行完EXEC之后,Redis实际执行这些操作时候,会报错。但是,注意:Redis对错误命令报错,但还是会把正确的命令执行完。在这种情况下,事务的原子性就无法得到保证。

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set score 100
QUEUED
127.0.0.1:6379(TX)> lpush score 200
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

上面的例子,命令和操作的数据类型不匹配的时候,set命令成功了,而lpush失败了。这个事务的原子性就没有得到保证。

DISCARD命令

到这里,你可能会疑惑,传统数据库(比如MySQL)在z执行事务的时候,会提供回滚机制,当事务执行发生错误的时候,事务中的所有操作都会被撤销,已经被修改的数据也会被恢复到事务执行前的状态。那么,在上面的例子中,若命令实际执行时报错了,是不是可以用回滚机制来恢复原来的数据呢?

其实,Redis没有提供回滚机制。Redis提供了DISCARD命令,但是,这个命令只能用来主动放弃事务执行,把暂存的命令队列情况,没起到回滚的效果。

执行EXEC时,Redis发生故障

事务的命令都是放入到一个命令队列中的,那么执行exec时候,执行了该事务的一半命令,而Redis出现故障,那这一半命令是执行成功的了。但是Redis出故障重启后,就需要通过RDB或者AOF文件去恢复数据。

  • AOF模式:现在一般都是开启AOF的,若开启了AOF的, Redis会首先去找AOF文件恢复数据。首先,这个事务的一半命令的操作记录是被记录到AOF日志中的了。但是这个事务是未完成的。使用redis-check-aof工具检测 AOF 日志文件,可以把未完成的事务操作从 AOF 文件中去除。这样一来,使用 AOF 文件恢复实例后,这个事务不会被再执行,从而保证了原子性
  • RDB模式:若是只使用RDB模式,在执行事务时,Redis 不会中断事务去执行保存 RDB 的工作,只有在事务执行之后,保存 RDB 的工作才有可能开始。即是最新的 RDB 快照是在当前 EXEC 执行之前生成的。所以当 RDB 模式下的 Redis 服务器进程在事务中途出故障时,事务内执行的命令,不管成功了多少,都不会被保存到 RDB 文件里。通过RDB文件去恢复,这样就不会再次执行该事务的命令语句,从而保证了原子性。
  • 不开启AOF和RDB:重启后内存中的数据就会全部丢失,也就谈不上原子性了。

Redis对事务原子性属性的保证情况

  • 命令入队时就报错,会放弃事务执行,保证原子性;
  • 命令入队时没有报错,在实际执行时报错,不保证原子性;
  • exec命令执行时,Redis实例故障,若开启了AOF或者RDB,可以保证原子性。

 

 2. 一致性

其会受到错误命令、实例故障的影响。有三种情况:

命令入队就错误

在这种情况下,事务本身就会被放弃执行,那么就可以保证数据库的一致性。

命令在实际执行时才报错

在这种情况下,有错误的命令不会被执行,正确的命令可以正常执行,所以也不会改变数据库的一致性。

EXEC命令执行时,Redis发生故障

故障后进行重启,就会使用RDB或AOF文件来恢复数据。

  • 纯内存模式。即是没有开启RDB或者AOF,,那实例故障重启后,数据就都没有了,数据库是一致的。
  • 使用RDB快照模式。因为RDB快照不会在事务执行时执行,所以,事务命令的操作不会被保存到RDB快照中。使用RDB恢复时候,数据库里的数据也是一样的。
  • 使用AOF。而事务操作还没有被记录到AOF日志时(即是命令已经有执行一条的,当时该记录还没有写入到磁盘中),实例就发生故障。所以使用AOF日志恢复的数据库日志时一致的。若是只有部分操作被记录到了AOF日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。

所以,总结来说,在命令执行错误或 Redis 发生故障的情况下,Redis 事务机制对一致性属性是有保证的。

 

3. 隔离性 

这个属性有讲究。一开始时候,我认为Redis的执行命令部分是单线程的,那就是串行化,那肯定是有隔离性保障的。但是却不是这么简单的。

看一个场景,商品库存是10,用户A开启事务,把库存-5的操作存入到命令队列中,这时用户B把库存-5,这个操作是即刻执行的。那在真正执行事务命令前,库存就是3了。之后exec,那库存再-5,那就不符合业务要求的,那就是没有隔离性。

所以, 不是说是单线程执行命令就是有隔离性保证的了。

Redis的隔离性保证,会受到和事务一起执行的并发操作的影响。而事务执行又可以分成 命令入队(exec命令执行前)命令实际执行(exec命令执行后) 两个阶段。

  • 并发操作在exec命令执行前,隔离性的保证要使用watch机制来实现,否则无隔离性(就是上面的例子)。
  • 并发操作在exec命令后,此时,隔离性可以保证。

那要讲讲watch机制。其作用是,在事务真正执行前,监控该事务会操作的一个或多个键的变化情况,当事务调用exec执行时,watch机制会先检查监控的键是否被其他客户端修改了,若是修改了,就放弃事务执行,避免事务的隔离性被破坏。然后,客户可以再次执行事务,若此时没有并发修改数据的操作了,事务就能正常执行,隔离性也得到了保证。

watch机制,对于用户来说,就是使用watch命令,当时需要用户主动显性使用watch监听想要操作的键。比如上面的事务是想要操作键shop,所以就需要在mutli之前执行 watch shop。

执行exec时候,返回nil,表示放弃事务执行。这样一来,事务的隔离性就可以得到保证了。

而另一种情况:并发操作在 EXEC 命令之后被服务器端接收并执行

这个就简单多了。因为Redis执行命令部分是单线程的,而且,exec命令执行后,Redis会保证先把命令队列中的命令执行完,之后才执行并发收到的命令。所以这时并发操作,不会影响到事务命令执行。所以,在该情况下,并发操作并不会破坏事务的隔离性。

 

4. 持久性

Redis是内存数据库,所以,其数据是否持久化保存是完全取决于Redis的持久化配置模式。

  • 没有使用RDB或者AOF,事务的持久性肯定是得不到保证的。
  • 使用RDB模式。在一个事务执行后,下一次的 RDB 快照还未执行前,如果发生了实例宕机,事务的持久性同样无法保证;
  • 使用 AOF 模式。AOF 模式的三种配置选项 no 、everysec 都会存在数据丢失的情况 。always 可以保证事务的持久性,但因为性能太差,在生产环境一般不推荐使用。

综上,redis 事务的持久性是无法保证的 。 

总结

Redis使用 multi、exec、discard、watch 四个命令来支持事务机制。

  • multi :开启一个事务
  • exec :提交事务,从命令队列中取出提交的操作命令,进行实际执行
  • discard : 放弃一个事务,清空命令队列
  • watch :检测一个或多个键的值在事务执行过程中是否发生变化,若发生变化,当前事务就放弃执行

Redis的事务机制可以保证一致性隔离性一定程度的原子性(不提供回滚),无法保证持久性。 

原子性的情况比较复杂,只有当事务中使用的命令执行时有误(即是入队无误,执行时刻有误),原子性得不到保证,其他情况下,事务都可以原子性执行。


http://www.ppmy.cn/server/31234.html

相关文章

Linux——命名管道

管道特点 只能用于具有具体祖先的进程之间的通信,通常,一个管道由一个进程创建,然后该进程调用fork,创建子进程,关闭相应的读写端,然后父子进程就可以通信了管道提供流式服务一般而言,进程退出…

企业计算机服务器中了devicdata勒索病毒怎么处理,devicdata解密数据恢复

网络技术的不断应用与发展,加快了社会进步的步伐,越来越多的企业利用网络开展各项工作业务,网络为企业提供了极大便利,大大提高了生产效率,网络数据安全问题成为了众多企业关心的主要话题。近日,云天数据恢…

QT创造一个新的类(柱状图的类),并关联属性和方法

1.以在UI上添加柱状图的类为例&#xff08;Histogram&#xff09; #ifndef STUDY_HISTOGRAM_H #define STUDY_HISTOGRAM_H#include <QVector> #include <QWidget>// 前向声明 QT_BEGIN_NAMESPACE class QColor; class QRect; class QString; class QPaintDevice; …

基于.NET WinForms 数据的CURD实现

开发工具 VS 2022 C#&#xff0c;数据库MS SQL SERVER 2019 1.WinForms界面 2.使用SqlDataApater DataSet DataGridView 读取数据 private void ReadData() {//数据库连接串string strConn "Data Source127.0.0.1;Initial CatalogTEST;Persist Security InfoTrue;Us…

python直接发布到网站wordpress之一只发布文字

本地环境&#xff1a;win10&#xff0c;python3.8.10。 远程服务器&#xff1a;是宝塔一键部署的wordpress6.5.2。 本地需要安装&#xff1a;pip38 install python-wordpress-xmlrpc。 本篇记录只发布文字&#xff08;不含图片&#xff09;&#xff0c;直接上代码&#xff1a…

关于Kotlin

Kotlin是一种由JetBrains公司开发的静态类型编程语言&#xff0c;它专为现代多平台应用设计。Kotlin的语法简洁&#xff0c;安全且实用&#xff0c;并且专注于与Java的互操作性。它几乎可以运行在任何Java语言可以运行的地方&#xff0c;但相比Java&#xff0c;Kotlin更加简洁、…

mac 安装anaconda

1. anaconda Anaconda是一个开源的Python和R编程语言的发行版本&#xff0c;用于数据科学、机器学习、大数据处理和科学计算等领域。它包含了一系列用于数据分析和科学计算的软件包、库和工具&#xff0c;以及一个用于管理环境和依赖关系的包管理系统。 Anaconda主要包括以下…

安卓抓包总结

背景 接手公司项目安卓xhs项目&#xff0c;该项目为android数据采集接口&#xff0c;该接口目前只能调用一次&#xff0c;然后就被平台风控&#xff0c;导致无法使用&#xff0c;需要恢复该接口继续使用&#xff0c;所以需要抓包分析调用的接口&#xff0c;对比接口参数和请求都…

Python中备选构造函数,classmethod和staticmethod的用法

在Python官方教程中并没有提及classmethod和staticmethod这两个装饰器。学过java面向对象编程的同学可能会觉得奇怪&#xff0c;为什么Python提供两个这样的装饰器&#xff0c;而不是只提供一个&#xff1f; 我们先来看看classmethod&#xff0c;它的用法&#xff1a;定义操作…

java设计模式 -- 工厂模式

1、基本概念 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一&#xff0c;这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 工厂模式提供了一种创建对象的方式&#xff0c;而无需指定要创建的具体类。 工厂…

数据仓库——聚集

在数据仓库中&#xff0c;聚集&#xff08;Aggregation&#xff09;是一个重要的概念&#xff0c;它涉及到对大量详细数据进行统计和汇总&#xff0c;以便更高效地执行查询和分析。以下是关于数据仓库中聚集的详细解释&#xff1a; 概念&#xff1a;聚集是指按照维度粒度、指标…

Android 编译文件简述(Android.mk)

Android 编译文件简述(Android.mk) Android.mk 文件是 Android 构建系统中的一种构建配置文件,使用 GNU Make 语法,用于定义 Android 项目中的模块、库、应用程序、JNI 代码等的编译和链接方式。每个 Android.mk 文件通常对应一个目录,代表构建系统应该如何处理该目录下的源…

一加12/11/10/Ace2/Ace3手机上锁回锁BL无限重启黑屏9008模式救砖

一加12/11/10/Ace2/Ace3手机官方都支持解锁BL&#xff0c;搞机的用户也比较多&#xff0c;相对于其他品牌来说&#xff0c;并没有做出限制&#xff0c;这也可能是搞机党最后的救命稻草。而厌倦了root搞机的用户&#xff0c;就习惯性回锁BL&#xff0c;希望彻底变回官方原来的样…

中间件之异步通讯组件RabbitMQ进阶

这里我们必须尽可能确保MQ消息的可靠性&#xff0c;即&#xff1a;消息应该至少被消费者处理1次 那么问题来了&#xff1a; 我们该如何确保MQ消息的可靠性&#xff1f; 如果真的发送失败&#xff0c;有没有其它的兜底方案&#xff1f; 首先&#xff0c;我们一起分析一下消息…

C#面:如何部署 ASP.NET 页面

在C#中部署 ASP.NET 页面有几种常见的方式&#xff0c;下面我将介绍其中两种常用的方式&#xff1a; IIS部署&#xff1a; 在服务器上安装IIS&#xff08;Internet Information Services&#xff09;。 在IIS中创建一个新的网站或虚拟目录&#xff0c;将其指向你的ASP.NET项目…

android zygote进程启动流程

一&#xff0c;启动入口 app_main.cpp int main(int argc, char* const argv[]) {if (!LOG_NDEBUG) {String8 argv_String;for (int i 0; i < argc; i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ")…

Python 植物大战僵尸

文章目录 效果图项目结构实现思路源代码 效果图 项目结构 实现思路 下面是代码的实现思路&#xff1a; 导入必要的库和模块&#xff1a;首先&#xff0c;我们导入了Python的os、time库以及pygame库&#xff0c;还有植物大战僵尸游戏中用到的各个植物和僵尸的类。 初始化游戏和…

UNIAPP小程序从入门到精通

第一章> 1、如何创建项目 2、项目的基本结构 3、页面组成&#xff08;wxss可以不用&#xff09; 4、组件的使用 5、协同开发与发布 第二章> 6、WXML页面结构渲染 7、style样式美化 8、a…

富格林:有效控制暗箱阻挠被骗

富格林悉知&#xff0c;当前现货黄金的行情波动较为激烈&#xff0c;对于我们投资者来说意味着投资盈利的机会多了&#xff0c;但同时投资暗箱风险亦随之而来。如果我们面对暗箱风险没有做好半点准备的话&#xff0c;可能会遭遇巨大损失甚至无法阻挠被骗。那么我们该如何阻挠被…

ASV1000视频监控平台:通过SDK接入海康网络摄像机IPC

目录 一、为何要通过SDK接入海康网络摄像机 &#xff08;一&#xff09;海康网络摄像机的SDK的功能 1、视频采集和显示 2、视频存储 3、视频回放 4、报警事件处理 5、PTZ控制 6、自定义设置 7、扩展功能 &#xff08;二&#xff09;通过SDK接入的好处&#xff08;相对…