1.什么是事务

        事务就是进行多个操作,要么同时执行成功,要么同时执行失败。

2.事务的特性 - ACID特性

2.1原子性Atomicity

        原子性(Atomicity):当前事务的操作要么同时成功,要么同时失败。原子性由undo log日志来实现。

        在事务中,如果SQL进行了一个减库存的操作,例如由5扣减为2,在执行这个SQL语句时会使用undo log日志进行记录一个库存2到5的日志,如果失败抛出异常就会执行这条SQL语句。

2.2一致性Aconsistency

        一致性(Consistency):使用事务的最终目的,由其它3个特性以及业务代码正确逻辑来实现。

2.3隔离性Isolation

2.3.1什么是隔离性

        隔离性(Isolation):在事务并发执行时,他们内部的操作不能互相干扰,隔离性由MySQL的各种锁以及MVCC机制来实现。

        在InnoDB引擎中,定义了四种隔离级别供我们使用,级别越高事务隔离性越好,但性能就越低,而隔离性是由MySQL的各种以及MVCC机制来实现的。

        1.read uncommit(读未提交):脏读。

        2.read commit(读已提交):不可重复读。

        3.repeatable read(可重复读):幻读。

        4.serializable(串行):解决上面所有问题,包括脏写。

        MySQL的事务隔离性默认使用的是repeatable read(可重复读)

2.3.2读未提交

        在read uncommit(读未提交)的隔离级别下,会产生脏读现象。

        脏读:事务A读取到事务B已经修改但是尚未提交的数据。

        一般不会使用这种隔离级别,这种隔离级别危机太多了。

2.3.3读已提交

        在read commit(读已提交)的隔离级别下,会产生不可重复读的现象。

        不可重复读的意思是,A事务在多次读取一个数据时,如果B事务在过程中对数据进行修改,并提交,A事务读到的数据会发生变化。

2.3.4可重复读

        在repeatable read(可重复读)的隔离级别下,会产生幻读的现象。

        执行的机制:在事务开启后,当执行了事务中第一条查询语句之后,MySQL中所有记录在事务中都有一条快照了,读取数据时都是读取快照中的数据,由此来实现的可重复读。

        还需要注意的是,如果在当前事务对数据进行了修改,下次再读取的时候就会使用修改后的值,其他未修改的数据还是读取快照中的数据。并且修改的时候,不会用快照的数据,而是真实的数据。

        由于该机制,A事务在执行完第一天查询语句后,读取的数据都是当时快照的数据,无论其他数据如何修改,在事务中读取的都是快照里的数据。

        而且还会出现幻读的现象,幻读现象就是事务A读取到了事务B刚新增的数据。

        有人说可重复读解决了幻读现象,也有人说可重复读没有解决幻读现象,这个是需要去分情况讨论的。现在同时启动A和B两个事务,假设A进行了一次查询操作,B事务在该表中进行了添加数据操作并提交了事务,这样A事务再去读取该表的数据时,由于A事务可重复读的机制,读取的数据是快照数据,则不会读取到B事务添加的数据,就不会产生幻读现象。但是如果A在开启事务后,查询了数据后,B事务去再去添加数据的时候,A事务再次去查询就会查询到B事务新增的数据,这样就会产生幻读现象。

2.3.5脏写现象

        读未提交、读已提交、可重复读都可能会出现脏写现象。

        脏写的意思是,由于隔离级别的问题,读取的数据是有问题的数据,但是还是在Java中对该数据进行处理后,并写入到了数据库中,此时写入的数据是个错的数据。

2.3.5.1读未提交出现脏写的原因

        读未提交会出现脏写现象是因为,事务A和事务B一起开启了,事务B进行对数据修改了多次,但是事务B并没有进行提交,事务A此时读到了事务B未提交的数据,但是在事务A读取后,事务B进行了回滚,但是事务A用错误的数据,进行了操作,并更新到数据库,由此就会发生一次脏读事件。

2.3.5.2读已提交出现脏写的原因

        读已提交会出现脏写现象是因为,事务A和事务B一起开启了,事务B进行对数据修改了多次,但是由于事务B没有进行提交,事务A读取到的数据永远是原始的数据,事务A对数据进行了操作,并提交,事务B此时用事务A未提交之前的数据进行修改,并更新到数据库,由此会发生一次脏读事件。

2.3.5.3可重复读出现脏写的原因

        可重复读会出现脏写现象是因为,事务A和事务B一起开启了,事务A查询了一次数据,事务B对该数据进行了多次修改,并提交,事务A又读取了一次数据,读取到的数据是快照数据,并没有读取到事务B提交后的数据,并且事务A对该次读取的数据进行了修改,并提交到数据库,由此会发生一次脏读事件。

2.3.5.4解决脏写的方案:悲观锁

        悲观锁解决方案,可以解决读未提交、读已提交和可重复读三种隔离级别的情况。

        解决脏写可以采用的方法是采用悲观锁,每次更新的时候,使用SQL语句进行更新,而不是使用查询到的数据,在业务代码中进行修改后直接更新替换数据。

        代码如下:

UPDATE account SET balance = balance + 500 WHERE id = 1;
2.3.5.5解决脏写的方案:乐观锁

        乐观锁的解决方案,可以解决读未提交和读已提交两种隔离级别的方案。

        解决脏写可以采用的方法是,给表格额外加一个version字段,每次更新的时候,WHERE筛选时加上自己上次查询出的版本号,如果更新成功就没事,更新失败就继续重新查询再更新,做一个自旋操作。

        代码如下:

UPDATE account SET balance = balance WHERE id = 1 AND version = 1;

2.3.6串行化读取

        在串行(serializable)情况下,这是最高的隔离级别,解决了以上所有的问题。

        串行化的机制就是,当A事务开启后,其隔离级别是serializable时,A事务只要对某个数据表进行了查询操作,其他任务对该表的增加/更新操作都会被卡住,直到A事务提交/回滚。

        这种方式解决了脏写和幻读的问题了,因为串行化对某个表进行读取操作后,其他针对该表的新增/删除/修改操作在串行化事务提交之前都会被阻塞住。

        串行化的实现原理:

        串行化的实现原理其实十分简单,在串行化事务中,执行是查询SQL语句会自动加一个读锁,由于读锁是共享的,当添加上这个读锁之后,对这个表再进行读取操作时,是没问题的,但是如果进行增加/修改/删除操作,就会被卡住,知道读锁释放。

        查询的语句:

SELECT * FROM account WHERE id = 1;

        该语句实际执行的时候,执行的SQL语句如下:

SELECT * FROM account WHERE id = 1 lock in share mode;

        一般不会去使用串行化这种方案,因为这个方案的性能太差了,整体并发量低。

2.3.7读写锁

        刚刚提到串行化隔离级别是使用读写锁实现的,所以现在具体介绍一下读写锁。

        读锁(共享锁、S锁):SELECT ... LOCK IN SHARE MODE;

        读锁是共享的,多个事务可以同时读取同一个资源,但是不允许其它事务修改。

        写锁(排它锁、X锁):SELECT ... FOR UPDATE;

        写锁是排他的,会阻塞其他的写锁和读锁,UPDATE、DELETE、INSERT都会加写锁。

2.4持久性Durability

        一旦提交了事务,它对数据库的改变就应该是永久性的。持久性由redo log日志来实现。

        MySQL保证持久性是通过redo log日志机制实现的,先来看MySQL更新一条数据的整体流程:

        InnoDB引擎在执行一条写数据的操作的时候(假设现在进行的是更新操作),首先会从磁盘中加载需要修改的数据,再将数据写入到undo日志中(方便进行回滚),下一步并不是直接去修改表中的数据,而是将数据写入到redo日志中,最后才通过异步IO线程将数据更新到磁盘文件中。

        之所以MySQL要做这个操作,完全是因为将数据写入到redo日志中进行的是磁盘顺序写的操作,而写入到表中的时候,由于多个表分布在多个文件中,无法进行磁盘顺序写,所以MySQL才会先将数据写入到redo日志中,对于机械硬盘来说,磁盘顺序写的性能提升很高,写入速度特别快。之所以要利用磁盘顺序写快速写入数据,是因为MySQL为了保证持久性,由于将数据写入到磁盘中是一个比较耗时的操作,如果中间出现突发情况,MySQL崩掉了,数据就会丢失,无法保证持久性,但是redo log是磁盘顺序写,性能非常高,所以可以保证持久性。

3.初识MVCC机制

3.1什么是MVVC机制

        事务分为RNC,RC,RR,Serializable四种,一般来说不会使用Serializable隔离级别,因为该隔离级别虽然安全性很高,但是其性能堪忧,所以一般在真正的项目落地时,都会采用RC和RR这两种隔离级别,因为这两种隔离级别可以保障读写的并发,所有操作并非都是串行的,可以提高整体的并发量。

        疑问点:RC和RR事务隔离级别在操作同一条数据的时候,是如何做到读写并发的呢?需不需要串行化呢?读写操作到底先执行读呢还是先执行写呢?

        以上提到的疑问点就是读写并发问题,MySQL使用MVVC机制解决了读写并发问题。

        MVVC(Multi-Version Concurrency Control)多版本并发控制,可以做到读写不阻塞,且避免了类似脏读这样的问题,主要是通过undo日志链来实现的。

        SELECT操作时快照读(读取历史版本)

        INSERT、UPDATE和DELETE是当前读(读取当前版本)

        Read Commit(读已提交):语句级快照。

        Repeatable Read(可重复读):事务级快照。

3.2MVVC机制的执行流程

        MySQL在每张数据表中维护了两个字段,其中一个字段是trx_id,即最后操作这条数据的事务ID,还有一个字段是roll_pointer,这个字段是回滚指针,该指针指向的是回滚日志,如果事务进行了回滚操作,就会通过数据行中的roll_pointer回滚指针,找到回滚日志,完成回滚操作。

        假设现在有一个数据表是account表,首先对account表进行一个插入操作,进行了插入操作之后,会讲操作account表的事务ID存储到数据行的trx_id字段中,并且会生成一个undo log日志,roll_pointer会指向该undo log。

        又有一个事务对数据行进行了操作,将数据行中的balance进行修改为500,此时会将数据行的trx_id进行替换,并又生成了一个undo log,并将roll_pointer指向该undo log,最后事务完成了提交,完成提交后,会有一个commited标志指向该数据行。

        假设现在启动了两个事务,事务A和事务B,事务A是RR可重复读隔离级别,事务B是RC读已提交隔离级别,此时两个事务同时执行查询该数据行的操作,最终查询出的数据均为500。

        此时又有事务对该数据行进行了操作,将balance修改为800,并进行了提交。事务A再进行查询的时候,由于RR的机制导致器读取到的数据是500,事务B读取数据时,由于其永远读取的是已提交的数据,所以读取到的数据是800。

        事务对该数据行再次进行了操作,将balance更新为1000但是没有提交,所以事务A和事务事务B再进行查询时保持原有数据不变。

3.3undo日志链

        从整个MVVC机制的执行流程中可以发现,无论是查询,回滚,是否提交等操作都是通过该链完成的,这样不仅节省了空间(不是每一个日志都要生成一个undo log),还保证了读写的并发性,提高了整体的性能。

4.如何选择适合隔离级别

        对于MySQL中四种隔离界别,RNC读未提交,RC读已提交,RR可重复读,Serilizable串行化,这四种隔离级别,性能从高到低,安全性从低到高,一般在实际生产落地的环境中不会使用到RNC读未提交,因为这种方式虽然性能很高,但是安全性特别差,会出现脏读现象,所以一般不会使用这种隔离级别。对于Serializable串行化隔离级别,一般在生产环境中也不会使用这种隔离级别,因为这种隔离级别安全性虽然很高,但是性能实在是太差了。

        在开发时,一般只会采用RC读已提交和RR可重复读这两种隔离级别。对于一些并发量要求不是巨大的系统,一般都是直接采用MySQL默认的隔离级别RR可重复读即可,但是对于一些互联网公司而言,如果需要很高的性能,会考虑采用RC读已提交这种隔离级别,因为这种隔离级别只会对写进行加事务,RR这种隔离级别会对读写都会加事务,所以相对来说RC的性能会更高一些。

        在实际事务选型时,需要根据当前业务场景进行分析选型。需要对读操作加事务的时候,可以采用RR事务隔离级别来保证事务内读数据时,读取出的数据在同一时间维度。如果不需要对读操作进行加事务的操作,可以根据需要考虑使用RC事务级别。

5.大事务的影响及事务优化手段

        并发情况下,数据库连接池容易被撑爆。

        由于事务在使用的时候,会占据着MySQL的一个链接,如果此时的并发特别高,且事务都非常大,每个事务需要占据着数据库连接很长一段时间,那么就会导致数据库连接池的连接全部被用完,最终数据库的连接池被撑爆。

        锁定太多的数据,造成大量的阻塞和锁超时。

        如果事务锁定了太多的数据,比如事务进行了多次update操作,UPDATE是会加锁的,加的锁会阻塞其他的事务对数据进行操作,其他事务会一直等待,但是如果事务特别大,让其它事务等待的时间过久,最终就会导致大量的阻塞和锁超时的事件发生。

        执行时间长,容易造成主从延迟。

        由于大事务的执行时间比较长,如果此时MySQL是主从多节点,就可能会出现主从延迟。

        回滚所需要的时间比较长。

        由于大事务中对数据库的操作比较多,所以一旦其中发生的错误,需要回滚的操作也会更多,这就导致了回滚所需要花费的时间比较长。

        undo log膨胀。

        大事务中对数据库的操作比较多,很多操作都会记录undo log日志,所以整条undo log日志链会膨胀,导致占用的空间较大。

        容易导致死锁。

        大事务中对数据库的操作比较多,很有可能引发死锁的出现。

5.2事务的优化实践原则

        将查询等数据准备操作放到事务外。

        如果查询操作需要使用RR隔离级别保证查询到的数据在同一时间维度时,是需要将查询操作放在事务内,但是如果查询操作不需要保证查询的数据在同一时间维度,使用RC隔离级别时,是可以将查询准数据的操作放到事务外的。

        事务中避免远程调用,远程调用要设置超时,防止事务等待时间太久。

        事务中进行执行业务代码的时候,是需要避免远程调用的,因为远程调用的接口的时间控制是不好控制的,如果等待时间过长,事务会膨胀为大事务,所以不能将远程调用放置在事务内,或者为远程调用设定超时时间。

        事务中避免一次性处理太多数据,可以拆分为多个事务分次处理。

        事务中如果一次性处理太多数据,事务可能会成为大事务,导致出现各种各样的问题,所以建议将事务拆分为多个事务分批次处理。

        更新等涉及加锁的操作尽可能放在事务靠后的位置。

        更新加锁操作是需要放在最后的,并且要放在Insert的后面,虽然Update和Insert两个操作都会加锁,但是Update加锁的时候会导致其他操作改行数据的操作卡住,但是Insert不会,因为没有插入的数据是不可能被其他事务操作的,自然不会出现卡住的现象

  • 能异步处理的尽量异步处理。
  • 应用侧(业务代码)保证数据一致性,非事务执行。

        如果系统对于性能的要求非常高,可以考虑使用try...catch等操作替代事务,使用业务代码实现事务的回滚功能,这样会显著提升性能。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025小学所有学习科目的全部版本电子教材

2025春小学最新课本-新版电子教材【文末自行获取全部资料~】 小学语文: 小学数学: 小学英语: 小学科学: 小学道德与法治: 小学劳动技术: 小学美术: 小学书法练习指导: 小学体育与健康…

华为视觉算法面试30问全景精解

华为视觉算法面试30问全景精解 ——技术引领 工程极致 智能未来:华为视觉算法面试核心考点全览 前言 华为作为全球领先的ICT(信息与通信技术)解决方案供应商,在智能终端、云计算、智慧城市、自动驾驶、工业互联网等领域持续推动视觉AI的创新与产业落地。华为视觉算法岗…

【Anaconda】Conda 虚拟环境打包迁移教程

Conda 虚拟环境打包迁移教程本文介绍如何使用 conda-pack 将 Conda 虚拟环境打包,并在另一台电脑上快速迁移、部署。0. 安装 conda-pack conda-pack 并非 Conda 默认自带工具,首次使用前必须手动安装。以下两种安装方式任选其一即可: ✅ 方法…

matrix-breakout-2-morpheus靶机通关教程

目录 一、信息搜集 二、尝试GetShell 三、反弹Shell 一、信息搜集 首先搜集信息,观察页面。 发现什么都没有,我们先来发现一下它的IP以及开放的端口。首先我们观察一下它的网络模式是怎么样的,来确定IP段。 可以发现他是NAT模式&#xff0…

深入思考【九九八十一难】的意义,试用歌曲能否解释

1. 《平凡之路》- 朴树契合点:前半生追求明白:“我曾经失落失望失掉所有方向,直到看见平凡才是唯一的答案”。后半生修行糊涂:“时间无言,如此这般,明天已在眼前”。对过去的释然与对未来的随缘&#xff0c…

SSM之表现层数据封装-统一响应格式全局异常处理

SSM之表现层数据封装-统一响应格式&全局异常处理一、为什么需要表现层数据封装?二、表现层数据封装的通用格式成功响应示例失败响应示例三、SSM中实现统一响应对象3.1 定义响应对象类(Result.java)四、全局异常处理4.1 实现全局异常处理器…

微软Fabric重塑数据管理:Forrester报告揭示高ROI

在数字化转型加速的今天,微软公司推出的Microsoft Fabric数据管理平台正以其卓越的经济效益和全面的技术能力引领行业变革。根据Forrester Consulting最新发布的总体经济影响(TEI)研究报告,该平台展现出令人瞩目的商业价值:实现379%的投资回报…

基于Qt和OpenCV的图片与视频编辑器

应用技术:Qt C、OpenCV、多线程、单例模式,qss样式表、OpenGL、ffmpeg。 本项目为Qt mingw6.5.3版本,QtCreator编写运行。 void XVideoWidget::do_setImage(cv::Mat mat) {QImage::Format fmt QImage::Format_RGB888;int pixSize 3;//处理…

NOTEPAD!NPCommand函数分析之comdlg32!GetSaveFileNameW--windows记事本源代码分析

第一部分:kd> kcUSER32!InternalCallWinProc USER32!UserCallDlgProcCheckWow USER32!DefDlgProcWorker USER32!SendMessageWorker USER32!InternalCreateDialog USER32!InternalDialogBox USER32!DialogBoxIndirectParamAorW USER32!DialogBoxIndirectParamW US…

【Qt开发】信号与槽(一)

目录 1 -> 信号和槽概述 1.1 -> 信号的本质 1.2 -> 槽的本质 2 -> 信号与槽的连接方式 2.1 -> 一对一 2.2 -> 一对多 2.3 -> 多对一 3 -> 小结 1 -> 信号和槽概述 在 Qt 中,用户和控件的每次交互过程称为一个事件。比如 “用户…

目标检测中的标签分配算法总结

目标检测中的标签分配算法是训练过程中的一个核心环节,它决定了如何将标注好的真实目标框分配给模型预测出来的候选框(Anchor Boxes或Points),从而为这些候选框提供监督信号(正样本、负样本、忽略样本)。它…

图片转 PDF三个免费方法总结

📌 为什么需要图片转 PDF? 在工作和生活中,我们经常需要将多张图片整理成 PDF 文档,例如:工作资料归档, 学习笔记整理,作品集展示,便捷分享。 方法一、iLoveOFD在线工具 提供图片…

Kafka 在分布式系统中的关键特性与机制深度解析

在分布式系统架构中,消息中间件扮演着 "数据枢纽" 的核心角色,而 Kafka 凭借其卓越的性能和可靠性,成为众多企业的首选。本文将深入剖析 Kafka 在分布式环境中的核心特性与底层机制,揭示其高吞吐、高可用的底层逻辑。一…

Python实战:基于Streamlit的股票筛选系统,实时K线图+数据缓存优化

基于 Streamlit 构建的股票筛选分析工具,整合了 Tushare 接口获取股票数据,并通过交互式界面实现股票筛选、信息展示和 K 线图分析。以下是深度解读:一、代码结构概览依赖库导入import streamlit as st import tushare as ts import pandas a…

网络安全威胁和防御措施

网络安全基础概念网络安全指保护网络系统及其数据免受未经授权的访问、破坏或泄露。涵盖硬件、软件、数据及服务的安全防护,涉及技术、管理和法律等多层面措施。常见网络安全威胁恶意软件:病毒、蠕虫、勒索软件等通过漏洞感染系统。网络钓鱼:…

Spring DeferredResult 实现长轮询

1、背景 在项目开发中,有一个流程性的方法执行,这个方法会调用各种方法,可能会导致时间比较长 ,如果一直等待响应结果的话,可能会造成超时,如果直接使用异步的方式的话,前端无法知道整体流程什…

Python设计模式 - 桥接模式

定义 桥接模式是一种结构型设计模式,它的核心思想是将抽象部分与实现部分分离,使它们可以独立变化。 结构抽象类(Abstraction):定义抽象接口,持有实现部分的引用。具体抽象类(Refined Abstracti…

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 用户注册实现

大家好,我是java1234_小锋老师,最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程,持续更新中,计划月底更新完,感谢支持。今天讲解用户注册实现 视频在线地址: …

华为7月23日机考真题

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 笔试突围OJ](bishipass.com) 03. 山峰观测站数据分析 问题描述 LYA是一名地理数据分析师,负责分析山峰观测站收集的海拔高度数据。观测站在一条直线上设置了…

图像分析学习笔记(4):机器学习图像特征与描述

图像分析学习笔记(4):机器学习图像特征与描述深度学习基础深度学习技巧深度模型构建深度学习基础 深度学习概念:深度学习是机器学习的一个分支,它基于一系列算法,试图通过使用多个处理层建立数据的高级抽象…