漫谈mysql中的事务

Fly,Mason!!! Fly,Mason!!!     2022-07-29     389

关键词:

 最近一直在做订单类的项目,使用了事务。我们的数据库选用的是MySql,存储引擎选用innoDB,innoDB对事务有着良好的支持。这篇文章我们一起来扒一扒事务相关的知识。

为什么要有事务?

        事务广泛的运用于订单系统、银行系统等多种场景。如果有以下一个场景:A用户和B用户是银行的储户。现在A要给B转账500元。那么需要做以下几件事:

            1. 检查A的账户余额>500元;

            2. A账户扣除500元;

            3. B账户增加500元;

        正常的流程走下来,A账户扣了500,B账户加了500,皆大欢喜。那如果A账户扣了钱之后,系统出故障了呢?A白白损失了500,而B也没有收到本该属于他的500。以上的案例中,隐藏着一个前提条件:A扣钱和B加钱,要么同时成功,要么同时失败。事务的需求就在于此。

事务是什么?

        与其给事务定义,不如说一说事务的特性。众所周知,事务需要满足ACID四个特性。

            1. A(atomicity) 原子性。一个事务的执行被视为一个不可分割的最小单元。事务里面的操作,要么全部成功执行,要么全部失败回滚,不可以只执行其中的一部分。

            2. C(consistency) 一致性。一个事务的执行不应该破坏数据库的完整性约束。如果上述例子中第2个操作执行后系统崩溃,保证A和B的金钱总计是不会变的。

            3. I(isolation) 隔离性。通常来说,事务之间的行为不应该互相影响。然而实际情况中,事务相互影响的程度受到隔离级别的影响。文章后面会详述。

            4. D(durability) 持久性。事务提交之后,需要将提交的事务持久化到磁盘。即使系统崩溃,提交的数据也不应该丢失。

事务的四种隔离级别

        前文中提到,事务的隔离性受到隔离级别的影响。那么事务的隔离级别是什么呢?事务的隔离级别可以认为是事务的"自私"程度,它定义了事务之间的可见性。隔离级别分为以下几种:

          1.READ UNCOMMITTED(未提交读)。在RU的隔离级别下,事务A对数据做的修改,即使没有提交,对于事务B来说也是可见的,这种问题叫脏读。这是隔离程度较低的一种隔离级别,在实际运用中会引起很多问题,因此一般不常用。

          2.READ COMMITTED(提交读)。在RC的隔离级别下,不会出现脏读的问题。事务A对数据做的修改,提交之后会对事务B可见,举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,会读到最新的数据2。在RC的隔离级别下,会出现不可重复读的问题。这个隔离级别是许多数据库的默认隔离级别。

          3.REPEATABLE READ(可重复读)。在RR的隔离级别下,不会出现不可重复读的问题。事务A对数据做的修改,提交之后,对于先于事务A开启的事务是不可见的。举例,事务B开启时读到数据1,接下来事务A开启,把这个数据改成2,提交,B再次读取这个数据,仍然只能读到1。在RR的隔离级别下,会出现幻读的问题。幻读的意思是,当某个事务在读取某个范围内的值的时候,另外一个事务在这个范围内插入了新记录,那么之前的事务再次读取这个范围的值,会读取到新插入的数据。Mysql默认的隔离级别是RR,然而mysql的innoDB引擎间隙锁成功解决了幻读的问题。

          4.SERIALIZABLE(可串行化)。可串行化是最高的隔离级别。这种隔离级别强制要求所有事物串行执行,在这种隔离级别下,读取的每行数据都加锁,会导致大量的锁征用问题,性能最差。

       为了帮助理解四种隔离级别,这里举个例子。如图1,事务A和事务B先后开启,并对数据1进行多次更新。四个小人在不同的时刻开启事务,可能看到数据1的哪些值呢?

           

                                                            图1

        第一个小人,可能读到1-20之间的任何一个。因为未提交读的隔离级别下,其他事务对数据的修改也是对当前事务可见的。第二个小人可能读到1,10和20,他只能读到其他事务已经提交了的数据。第三个小人读到的数据去决于自身事务开启的时间点。在事务开启时,读到的是多少,那么在事务提交之前读到的值就是多少。第四个小人,只有在A end 到B start之间开启,才有可能读到数据,而在事务A和事务B执行的期间是读不到数据的。因为第四小人读数据是需要加锁的,事务A和B执行期间,会占用数据的写锁,导致第四个小人等待锁。

        图2罗列了不同隔离级别所面对的问题。

        

                                                       图2

        很显然,隔离级别越高,它所带来的资源消耗也就越大(锁),因此它的并发性能越低。准确的说,在可串行化的隔离级别下,是没有并发的。

                

                                                         图3

MySql中的事务

        事务的实现是基于数据库的存储引擎。不同的存储引擎对事务的支持程度不一样。mysql中支持事务的存储引擎有innoDB和NDB。innoDB是mysql默认的存储引擎,默认的隔离级别是RR,并且在RR的隔离级别下更进一步,通过多版本并发控制(MVCC,Multiversion Concurrency Control )解决不可重复读问题,加上间隙锁(也就是并发控制)解决幻读问题。因此innoDB的RR隔离级别其实实现了串行化级别的效果,而且保留了比较好的并发性能。

       事务的隔离性是通过锁实现,而事务的原子性、一致性和持久性则是通过事务日志实现。说到事务日志,不得不说的就是redo和undo。

     1.redo log

       在innoDB的存储引擎中,事务日志通过重做(redo)日志和innoDB存储引擎的日志缓冲(InnoDB Log Buffer)实现。事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。

        在系统启动的时候,就已经为redo log分配了一块连续的存储空间,以顺序追加的方式记录Redo Log,通过顺序IO来改善性能。所有的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起。如下一个简单示例:

        记录1:<trx1, insert...>

        记录2:<trx2, delete...>

        记录3:<trx3, update...>

        记录4:<trx1, update...>

        记录5:<trx3, insert...>

      2.undo log

        undo log主要为事务的回滚服务。在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。

        以下是undo+redo事务的简化过程

        假设有2个数值,分别为A和B,值为1,2

        1. start transaction;

        2. 记录 A=1 到undo log;

        3. update A = 3;

        4. 记录 A=3 到redo log;

        5. 记录 B=2 到undo log;

        6. update B = 4;

        7. 记录B = 4 到redo log;

        8. 将redo log刷新到磁盘

        9. commit

        在1-8的任意一步系统宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。如果在8-9之间宕机,恢复之后可以选择回滚,也可以选择继续完成事务提交,因为此时redo log已经持久化。若在9之后系统宕机,内存映射中变更的数据还来不及刷回磁盘,那么系统恢复之后,可以根据redo log把数据刷回磁盘。

        所以,redo log其实保障的是事务的持久性和一致性,而undo log则保障了事务的原子性。

分布式事务

        分布式事务的实现方式有很多,既可以采用innoDB提供的原生的事务支持,也可以采用消息队列来实现分布式事务的最终一致性。这里我们主要聊一下innoDB对分布式事务的支持。

        

        如图,mysql的分布式事务模型。模型中分三块:应用程序(AP)、资源管理器(RM)、事务管理器(TM)。

        应用程序定义了事务的边界,指定需要做哪些事务;

        资源管理器提供了访问事务的方法,通常一个数据库就是一个资源管理器;

        事务管理器协调参与了全局事务中的各个事务。

        分布式事务采用两段式提交(two-phase commit)的方式。第一阶段所有的事务节点开始准备,告诉事务管理器ready。第二阶段事务管理器告诉每个节点是commit还是rollback。如果有一个节点失败,就需要全局的节点全部rollback,以此保障事务的原子性。

总结

        什么时候需要使用事务呢?我想,只要业务中需要满足ACID的场景,都需要事务的支持。尤其在订单系统、银行系统中,事务是不可或缺的。这篇文章主要介绍了事务的特性,以及mysql innoDB对事务的支持。事务相关的知识远不止文中所说,本文仅作抛砖引玉,不足之处还望读者多多见谅。

 

参考文献:

 《高性能mysql第三版》

 《mysql技术内幕 innoDB存储引擎》

使用以下代码可以实现 MySQL 中的 PHP“嵌套”事务?

】使用以下代码可以实现MySQL中的PHP“嵌套”事务?【英文标题】:PHP"Nested"transactionsinMySQLcouldbearealityusingthefollowingcode?【发布时间】:2017-02-0920:34:23【问题描述】:好的,我正在使用PHP在MySQL中寻找“嵌套”事务的解决方... 查看详情

MySQL:存储过程中的事务

】MySQL:存储过程中的事务【英文标题】:MySQL:transactionwithinastoredprocedure【发布时间】:2012-04-1523:44:25【问题描述】:我的存储过程的基本结构是,BEGIN..Declarestatements..STARTTRANSACTION;..Query1....Query2....Query3..COMMIT;ENDMySQL版本:5.1.61-0u... 查看详情

mysql中的事务控制机制(代码片段)

事务控制是MySQL的重要特性之一。在MySQL中,InnoDB和NDBCluster是常见的事务型存储引擎。1.自动提交默认情况下,MySQL是自动提交(autocommit)的。也就意味着:如果不是显式地开始一个事务,每个查询都会被... 查看详情

如何以 root 身份终止 MySql 中的事务?

】如何以root身份终止MySql中的事务?【英文标题】:HowdoIkillatransactioninMySqlasroot?【发布时间】:2014-12-1601:40:48【问题描述】:我使用的是MySql5.5.37。作为root,我正在尝试终止一个锁定某些表的事务。我跑SELECT*FROMINFORMATION_SCHEMA.INN... 查看详情

PDO / PHP / MySQL 中的性能:事务与直接执行

】PDO/PHP/MySQL中的性能:事务与直接执行【英文标题】:PerformanceinPDO/PHP/MySQL:transactionversusdirectexecution【发布时间】:2010-11-2718:52:58【问题描述】:我正在循环多个值(例如1到100)并在循环内执行准备好的语句。与在循环内直接... 查看详情

mysql中的事务是啥如何使用

什么是事务?事务是逻辑上的一组操作,组成这组操作的各个单元,要不全都成功要不全都失败,这个特性就是事务注意:mysql数据支持事务,但是要求必须是innoDB存储引擎解决这个问题:mysql的事务解决这个问题,因为mysql的事... 查看详情

事务是不是会停止 MySQL 中的所有竞争条件问题?

】事务是不是会停止MySQL中的所有竞争条件问题?【英文标题】:DoesatransactionstopallraceconditionproblemsinMySQL?事务是否会停止MySQL中的所有竞争条件问题?【发布时间】:2010-05-1107:29:35【问题描述】:考虑这种情况:开始交易使用auto... 查看详情

17mysql事务

...(Atomicity):一个事务是一个不可分割的工作单位,事务中的所有操作要么全部完成,要么全部不完成。一致性(Consistency):当事务完成时,所有数据都必须处于一致的状态,即数据库从一个一致性状态转移到另一个一致性状... 查看详情

在 haproxy 中的写入故障转移期间,正在进行的 mysql 事务会发生啥?

】在haproxy中的写入故障转移期间,正在进行的mysql事务会发生啥?【英文标题】:Whathappenstoin-flightmysqltransactionsduringawritefailoverinhaproxy?在haproxy中的写入故障转移期间,正在进行的mysql事务会发生什么?【发布时间】:2016-03-0421:38... 查看详情

mysql中的锁机制

介绍锁机制技术是为了解决问题而生的,锁被用来实现隔离性,保证并发事务的正确性。两段锁&一次封锁两段锁数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)加锁阶段:在加锁阶... 查看详情

mysql事务grant权限

...丢失。3.隔离性在执行过程中对外界保持隔离,操作过程中的数据不能互相访问//事务之间未提交数据不是相互可见3#隔离有一定的优先级别,默认是最高级别:不可访问操作中的数据;4.持久性通过事务提交的数 查看详情

mysql中的锁机制(代码片段)

介绍锁机制技术是为了解决问题而生的,锁被用来实现隔离性,保证并发事务的正确性。两段锁&一次封锁两段锁数据库遵循的是两段锁协议,将事务分成两个阶段,加锁阶段和解锁阶段(所以叫两段锁)加锁阶段:在加锁阶... 查看详情

关于mysql事务中的自增id的疑问

参考技术Aid自增,name为字符串类型table依然为空,开启事务后在没有commit的情况下,是没有修改table的。这里将事务b直接提交由于事物b提交了,插入了一行数据,id为2,所以这里生成id是在插入的时候。这个时候开始提交事物a... 查看详情

事物再次深刻理解

##msyql事务1.mysql:传统理解mysql中的一次操作过程(sql执行)是一次事务。2.mysql:那么多个线程同时操作mysql中的数据(同一条数据,一个范围内数据)就叫并发事务。3.mysql:数据库层面使用不同的事务隔离级别来进行并发事务的控制... 查看详情

mysql事务对查询有没有影响

...务是一种数据库技术,它可以保证一组SQL语句在执行过程中的原子性,即要么全部执行,要么全部不执行。这意味着,当您使用MySQL事务时,您可以确保您的查询不会被中断,从而保证查询的完整性和一致性。MySQL事务还可以帮... 查看详情

mysql事务并发问题和mvcc机制(代码片段)

...务读取到其他事务没有提交的数据举个栗子事务A读取表中的name,事务B将表中的张三修改称为了张老三,假设这时候事务A读取到了name为张老三,但是到最后事务B又因为其他原因回滚了数据,数据库表中的name为张三,那这就出现了问题... 查看详情

mysql事务

...的快照。  1.1事务的快照,是基于整个库的。2数据库中的数据是有多个版本的,每次更新的时候,都会生成行的一下事务的id和行数据,老的事务id和行数据也是被保    留的(这里的行数据其实是根据数据更新时候... 查看详情

mysql事务详解

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