mysql 事务详解一

前言

提到事务,大家肯定不陌生。在我们现实生活中也是存在的,比如我们去超市购物,然后去支付。虽然是两个步骤,必须保证同时成功,这个交易才可以完成。

如果这个场景,拿到我们购物系统,就是几张表订单表、支付表、流水表。你往订单表插入一条数据,同时也会往支付表,流水表拆入一条数据,这个插入操作必须同时成功。这就是事务,简单来说事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在mysql 中,事务支持是在引擎层实现的。并不是所有的引擎都支持事务,比如说Innodb 支持而myisam不支持,这也是Innodb取代myisam的原因。

不仅mysql 支持事务,很多数据库 Mongodb也支持,虽然他们实现的方式不一样,我会在接下来的文章详细阐述。今天我们主要讲的是mysql 事务。

隔离性与隔离级别

一个数据系统支不支持事务,主要有四大特性决定的,ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)。只要支持四大特性,我们就说系统支持事务。

大家要去了解这几个特性,首先必须明白mysql 的几种日志:redolog(重做日志),binlog(归档日志),undolog(回滚日志)

Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。是通过redolog和undolog实现的

Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。

Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。 是通过undolog 实现的

Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。 是通过redlog 和 binlog 实现的

当数据库多个事物同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题。

脏读:读到其他事物未提交的数据

不可重复读:同一个事物前后读到的数据内容不一致

幻读:前后读取的记录数量不一致。也就是说感知到了其他事物对数据的insert,delete 操作

为啥会有那么多的问题,主要是于事物的隔离级别有关

事务有以下几种隔离级别:

读未提交(read uncommitted):一个事务还没提交时,它做的变更就能被别的事务看到。

读提交(read committed):一个事务提交之后,它做的变更才会被其他事务看到。

可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。

串行化(serializable):顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

读提交和可重复读比较难以理解,这两种隔离级别也是用的最多的。他们的实现都与一种技术(mvcc)有关,关于这个话题,我会在接下来的文章继续阐述。读提交他解决了脏读的问题,可重复度解决了事务的不可重复度的问题,同时它通过间隙锁解决了幻读的问题。其他的两种隔离级别都没有用到mvcc,读未提交会产生大量的脏数据,用的少。串行化性能太低,事务串行化执行,并发下的效率。接下来我们将通过例子详细阐述这几种隔离级别。

有这样一个表

mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);

读未提交

事务A事务B
启动事务 查询得到值1启动事务
查询得到值1
将1改成2
查询得到值V1=2
提交事务B
查询得到值v2=2
提交事务A
查询得到值v3=2

通过上面的例子可以看到事务B还没有提交,事务A就看到了结果

读提交

事务A事务B
启动事务 查询得到值1启动事务
查询得到值1
将1改成2
查询得到值V1=1
提交事务B
查询得到值v2=2
提交事务A
查询得到值v3=2

通过上面的例子可以看到事务B提交后,事务A就可以看到事务B提交的数据

可重复度

事务A事务B
启动事务 查询得到值1启动事务
查询得到值1
将1改成2
查询得到值V1=1
提交事务B
查询得到值v2=1
提交事务A
查询得到值v3=2

通过上面的例子可以事务在执行期间看到的数据前后必须是一致的。在事务A中v2和v1的数据和它查询的数据保持一致

串行化(serializable)

事务A事务B
启动事务 查询得到值1启动事务
查询得到值1
将1改成2
查询得到值V1=1
提交事务B
查询得到值v2=1
提交事务A
查询得到值v3=2

通过上面的例子:事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。也就是说事务A执行完了事务B才开始执行

在实现上,数据库会在读提交和可重复度隔离级别下创建一个视图,访问的时候以视图的逻辑结果为准。在可重复读隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,在每个sql 语句开始执行的时候创建的。读未提交每次都是返回记录最新的数据。串行化是用加锁的方式避免并发访问。

由此可见不同的隔离级别,数据库的行为是不同的。大家应该选择数据库合适的隔离级别。mysql 默认的隔离级别是可重复读,oracle 默认的隔离级别是读提交。不过现在很多厂把mysql 的隔离级别改成读提交,这是为了减少锁冲突,可重复读为了解决幻读引入了复杂的锁机制。

可以通过下列方式查看数据库的隔离级别

mysql> show variables like 'transaction_isolation';ERROR 4031 (HY000): The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.No connection. Trying to reconnect...Connection id: 283Current database: zipkin+-----------------------+-----------------+| Variable_name | Value |+-----------------------+-----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+-----------------+1 row in set (0.07 sec)

可重复读的隔离级别也不是没有人用,试想下面的场景:

假设你在管理一个个人银行账户表。一个表存了账户余额,一个表存了账单明细。到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。

这时候使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。

事务隔离的实现

在mysql 中,实际上每条记录在更新的时候都会记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。这些数据通过事务id记录在undolog中,这就是所谓的回滚日志

当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。

你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。基于上面的说明,

我们来讨论一下为什么建议你尽量不要使用长事务。长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有 200GB 的库。最终只好为了清理回滚段,重建整个库。除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

事务的启动方式

1、显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。这个时候事务并没有开始,事务的开始是它们之后的第一个操作 InnoDB 表的select 语句启动。如果想马上启动一个事物用start transaction with consistent snapshot这个命令

2、set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。

有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。因此,我会建议你总是使用 set autocommit=1, 通过显式语句的方式来启动事务。

对于一个需要频繁使用事物的业务。我们可以用 commit work and chain 语法减少语句的交互次数,就会在提交的时候开始下一个事物,也省去了再次执行 begin 语句的开销

执行的事物都在 information_schema 库的 innodb_trx 我们可以用下列语句查询正在执行的事物。查询持续时间大于60s的事物,这个可以查询长事物

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

这篇文章我们已经降到了事物,那么我们接下来思考几个问题:

1、使用长事务的弊病? 为什么使用常事务可能拖垮整个库?

1)长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

2)长事务更容易导致死锁,会进行大量的死锁检测,消耗大量cpu,影响系统性能。关于死锁的话题可以阅读mysql 锁详解-CSDN博客 这篇文章

3)容易造成超时,并使业务不断重试,使系统造成的很大的压力

4)会占用mysql 的连接,mysql 的连接得不到释放,会对业务的造成不好的影响

2、如何避免长事务的出现?

从数据库方面:

a.设置autocommit=1,不要设置为0。

b.写脚本监控information_schemal.innodb_trx表中数据内容,发现长事务,kill掉它。

c.配置SQL语句所能执行的最大运行时间,如果查过最大运行时间后,中断这个运行事情长的SQL语句。

d.设置回滚表空单独存放,便于回收表空间。

从业务代码方面:

a.确认是否使用了autocommit=0的配置,如果有关闭它,然后再业务代码中手动的使用begin;commit来操作。

b.检查业务逻辑代码,能拆分为小事务的不要用大事务。

c.检查代码,把没有必要的select语句被事务包裹的情况去掉。

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

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

相关文章

浅拷贝导致的bug

错误代码: //初始化formTableData的值 const formTableData ref({saleOrderTime:,saleOrderDetails:[] });const showModal async (item) > {//调接口获取后端返回的数据let data (await api.searchSaleOrderById({saleOrderId:item.id})).dataconsole.log(&…

零样本带解释性的医学大模型

带解释性的医学大模型 提出背景解法拆解方法的原因对比以前解法 零样本带解释性的医学大模型如何使用CLIP模型和ChatGPT来进行零样本医学图像分类用特定提示查询ChatGPT所生成的医学视觉特征描述相似性得分在不同症状上的可视化,用于解释模型的预测注意力图的可视化…

使用python查看官网是否发布新的内容

目录 前言 第一章、python介绍和使用pip install下载包 1.python介绍 2.使用vscode编写python 3.pip install的使用 第二章、查看官网是否发布新的内容 第三章、代码实现 目录结构 代码实现 check_new_news.py files.py news.py main.py file.txt 运行演示 前言 也…

基于飞凌嵌入式RK3568核心板的边缘计算门禁屏解决方案

边缘计算作为一种将计算任务从云端推向网络边缘的新型计算模式,正日益受到各行各业的青睐,并已在我们的生产和生活当中得到了广泛的应用,其中“门禁系统”就是最常见的与边缘计算相结合的应用之一。 传统的门禁系统受限于数据处理能力和网络…

【C语言】详解计算机二级c语言程序题

文章目录 前言资料相关程序题 一(字符串)程序题 二(数组)程序题 三(基础)程序题 四(结构体)程序题 五(结构体)程序题 六(基础) 前言 …

运放的虚短和虚断

上图中,线性区的这条斜线的斜率,就是开环增益(或者叫开环放大倍数),对于理想运放,其开环增益为正无穷,当然对于市面上的运放产品,斜率是不可能无穷大的,一般为几万~几百万…

【区块链】智能交易模式下的数据安全流通模型

【区块链】智能交易模式下的数据安全流通模型 写在最前面**区块链智能交易模式概述****数据安全流通的挑战****数据安全流通模型的核心要素****实现数据安全流通的区块链技术****区块链智能交易模式下数据安全流通模型的设计原则****数据安全流通模型的应用案例分析****面临的挑…

【蓝牙协议栈】btsnoop 概念介绍

1. btsnoop 概念介绍 btsnoop 用于记录蓝牙协议栈跟芯片交互的数据。在分析蓝牙问题的时候有很大的用途,能够快速定位问题所在,一般协议栈都有整合这个或者类似功能,否则我不认为这个协议栈是一个合格的协议栈,在 android 手机中…

高刷电竞显示器 - HKC VG253KM

今天给大家分享一款高刷电竞显示器 - HKC VG253KM。 高刷电竞显示器 - HKC VG253KM源于雄鹰展翅翱翔的设计灵感,严格遵循黄金分割比例的蓝色点晴线条,加上雾面工艺及高低起伏错落有致的线条处理,在VG253KM的背部勾勒出宛若大鹏展翅的鹰翼图腾…

DBeaver的下载安装和连接MySQL数据库

DBeaver的下载安装和连接MySQL数据库 1、dbeaver的下载 dbeaver是一款的数据库连接工具,免费,跨平台。 官网:https://dbeaver.io/ 下载地址:https://dbeaver.io/download/ GitHub下载地址:https://github.com/dbeav…

全面解析企业财务报表系列之四:财务报表的真实性和可靠性

全面解析企业财务报表系列之四:财务报表的真实性和可靠性 一、什么是会计方法二、选择会计方法三、会计方法的重要性四、会计报表常用的造假手段五、财务报表经常被遗漏的重要事件六、财务报告造假的资信敏感性七、财务报告审计的重要性八、审计报告 一、什么是会计…

更简单地介绍 CUDA

这篇文章是对 CUDA 的超级简单介绍,CUDA 是 NVIDIA 流行的并行计算平台和编程模型。我之前在2013年写过一篇文章《CUDA简单介绍》,多年来一直很受欢迎。但 CUDA 编程变得更加容易,GPU 也变得更快,所以是时候进行更新(甚…

家政小程序开发:帮助企业打造专属品牌,提升知名度

随着当下消费观念的升级,人口老龄化的严重,家政服务成为当下年轻人的必不可少的选择,我国家政服务市场的发展前景非常广阔。 如今,消费者对家政的需求日益多样化,家政市场数字化转型将成为一大发展趋势。在互联网等信…

第七章 本地方法栈

第七章 本地方法栈 1. 本地方法栈 Java虚拟机栈用于管理Java方法的调用,而本地方法栈用于管理本地方法(第六章本地方法)的调用。本地方法栈,也是线程私有的。允许被实现成固定或者是可动态扩展的内存大小。(在内存溢出方面是相同的) 如果线程请求分配的…

2024-2-22 作业

作业要求: 复习前面知识点(指针、结构体、函数)整理思维导图顺序表(按位置插入、按位置删除和去重、重新写)理解链表的代码,尝试写一下链表的尾插和输出 1.复习前面知识点(指针、结构体、函数) 2.整理思维导图 3.顺序表(按位置插入、按位置删除和去重、…

PyTorch概述(六)---View

Tensor.view(*shape)-->Tensor 返回一个新的张量同之前的张量具有相同的数据,但是具有不同的形状;返回的张量同之前的张量共享相同的数据,必须具有相同数目的元素,可能具有不同的形状;对于经过view操作的张量&…

2024Node.js零基础教程(小白友好型),nodejs新手到高手,(八)NodeJS入门——http模块

一念心清净,处处莲花开。 055_http模块_网页资源加载基本过程 哈喽,大家好,这一课节我们来介绍一下网页资源加载的基本过程。首先先强调一点,这个内容对于我们后续学习非常非常的关键,所以大家务必要将其掌握。 首先先…

llm的inference(一)

文章目录 前提LLMLLM结构1.Encoder-only2.Encoder-Decoder3.Decoder-only 宏观层面的LLM推理过程宏观推理过程的进一步详细说明从字符串输入到网络的输出 总结参考链接 前提 对LLM(大语言模型)的推理不太清楚,自己把遇到的和推理相关的知识做个总结,如有…

Autoencoder深度学习中的无监督学习神经网络

在当今的深度学习领域中,自动编码器(Autoencoder)是一种常见的无监督学习神经网络模型,用于学习有效的数据表示。自动编码器在许多领域都有广泛的应用,包括特征提取、降维、图像去噪、生成模型等。 自动编码器的基本原…

Servlet使用Cookie和Session

一、会话技术 当用户访问web应用时,在许多情况下,web服务器必须能够跟踪用户的状态。比如许多用户在购物网站上购物,Web服务器为每个用户配置了虚拟的购物车。当某个用户请求将一件商品放入购物车时,web服务器必须根据发出请求的…