MySQL事务原理实现与隔离级别

摘要

  本文主要讲解MySQL中的事务相关知识点。本文首先介绍什么是事务,以及事务有哪些属性,从而使读者理解MySQL事务。同时,描述了在并发情景中事务可能出现的问题有哪些,最后讲解了解决这些问题的方法,也就是事务的隔离级别。

  本人还写了MySQL相关博文,有兴趣的研友可以点击如下链接,请各位研友指正并留言。
   MySQL索引及优化
   MySQL的锁及其MVCC

一、事务的定义

   事务是将一系列操作当作最小的、不可分割的工作单元。请读者注意一个词“当作”,因为本人觉得一个事务并不是逻辑上不可分割的,事务在业务层面上才是最小、不可分割的。
  举个例子:Damon的银行账户向Tom的银行账户转100元。
  在这个例子中,按生活中的逻辑是可以分为两步完成,第一步Damon银行账户减100;第二步Tom银行账户加100。而从银行业务层面上看,Damon向Tom转100是一次性完成的,也就是逻辑上的两步必须同时完成,或同时失败,否则业务上是有漏洞的。
  事务也可以看作是关系型数据库区别于No-SQL数据库的重要特点之一。

二、事务的属性

  关于事务的属性,前人已为我们总结了如下四点:

  • 原子性(Atomicity):事务是最小单位,不可再分;
  • 一致性(Consistency):事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败
  • 隔离性(Isolation):事务A和事务B之间是互不干扰的;
  • 持久性(Durability):事务一旦提交,它对数据库的改变就应该是永久性的。

  关于事务的原子性和一致性已在第一节的例子中提到,读者若是不清楚,可以回到第一节的例子中体验、感受一下原子性和一致性。下面来解释隔离性和持久性。
  当Damon向Tom转100的同时Jam也向Tom转100,那么将看作两个事务,事务1:Damon向Tom转100;事务2:Jam向Tom转100。两个事务不能相互干扰,也就是其中一个事务的成功与否都另一个事务的成功与否没有关系。这便是隔离性。
  一个事务成功了,它的数据信息将保持到硬盘中,不会出现过了三分钟,系统中的事务数据信息丢失或改动。这就是持久性。

三、事务属性的实现原理

  在事务的四个特点中,一致性是事务的根本追求,而在某些情况下会对事务的一致性造成破坏:

  1. 事务的并发执行;
  2. 事务故障或系统故障。

  数据库系统通过并发控制技术和日志恢复技术来避免这种情况的发生:

  1. 并发控制技术保证了事务的隔离性,使数据库的一致性状态不会因为并发执行的操作被破坏;
  2. 日志恢复技术保证了事务的原子性,使一致性状态不会因事务或系统故障被破坏,同时使已提交的对数据库的修改不会因系统崩溃而丢失,保证了事务的持久性。

  事务的四个特性实现时所用的技术如图所示:

  • 原子性通过Undo Log来实现;
  • 持久性通过Redo Log来实现;
  • 隔离性通过读写锁+MVCC来实现;
  • 一致性通过原子性、持久性、隔离性来实现。

3.1 原子性的实现原理

  原子性是根据Undo Log来实现的。原子性强调的是一个事务中某个操作失败,需要将该事务中所有操作进行回滚。如何进行回滚呢?MySQL在开启事务时,首先将数据备份到一个文件中,该文件称为Undo Log,然后进行数据修改,如果出现错误或用户执行ROLLBACK语句,系统可以利用Undo Log中的备份,将数据恢复到事务开始之前的状态。
  Undo Log是逻辑日志,可以理解为:

  1. 当delete一条数据时,Undo Log会记录一条对应的insert操作;
  2. 当insert一条数据时,Undo Log会记录一条对应的delete操作;
  3. 当update一条数据时,Undo Log会记录一条对应update操作;

  比如需要执行Tom账户加100的操作,那么在操作前会在undo log记录Tom账户减100,那么事务回滚时安装Undo Log中的操作记录执行即可,即Tom账户减100,数据便能恢复到事务开启之前的状态。所以Undo Log可以保证事务的原子性。

3.2 持久性的实现原理

  持久性是根据redo log来是实现的。
  这与MySQL的数据存储过程有关。MySQL为了提高IO的效率,就使用了一个缓冲区buffer pool,MySQL的需要读取或写入数据时,都经过buffer pool,再由buffer pool对磁盘数据进行操作,这样就减少了磁盘的访问次数,提高了IO效率。
  但是存在一个问题,当MySQL的服务意外关闭而buffer pool中的数据并没有完全更新到磁盘数据中时,这就不能保证事务的持久性,于是MySQL又用了redo log来解决该问题,MySQL提交事务时,会将相应的数据操作写入buffer pool的同时也写入了redo log文件,所以即使MySQL的服务意外关闭,redo log文件也不会消失,服务重新开启时,MySQL会继续从redo log中对磁盘数据进行更新,从而保证了数据的持久性。

3.3 隔离性的实现原理

  隔离性是根据MySQL中的锁机制来实现的。
  使用锁来防止事务与事务之间的相互影响。锁可以分为表锁和行锁,行锁又可以分为读锁、写锁、间隙锁等等,锁的介绍会单独放到一个博文中,暂不详解。

3.4 一致性的实现原理

  一致性是通过原子性、持久性、隔离性来实现的。

三、并发情景中的事务问题

  随着用户访问量的剧增,常常伴随着并发应用情景。那么事务与事务之间在并发情景中会出现什么问题呢?可大致归纳为如下三种情况:

  1. 脏读:一个事务读取了另一个事务还未提交的修改数据。
      比如事务1:Damon向Tom转100;事务2:Jam向Tom转100。Tom的账户余额在两个事务之前为1000,现在事务2执行到Tom的余额加100时,Tom的余额将变为1100,但事务2还没有提交,此时事务1中读取Tom的余额时,却变为了1100,与事务1开始前Tom的余额不一致。这就是脏读。
  2. 不可重复读:一个事务读取了另一个事务已提交的修改数据。
      同上例, 事务1、事务2同时开始,事务2执行并提交了数据,当事务1再次读取Tom的余额时,已变为1100,表现出事务1不能重复读取到Tom的相同余额值。当另一个事务对该数据操作并提交时,事务1中的数据又发生了变化,这也将引发错误。
  3. 幻读:事务1对数据进行了添加(如,增加一行数据并非修改)但未提交,事务2读取到了这条添加的数据,随后事务1进行了回滚。那么事务2 就产生了幻读数据。

四、事务隔离级别

  MySQL的存储引擎InnoDB针对前文提及的事务并发问题,提供了四种事务隔离级别,分别为:

  1. 读未提交(read-uncommitted):一个事务可以读到另一个事务修改但未提交的数据;
  2. 读已提交(read-committed):一个事务只能读到另一个事务修改并提交的数据;
  3. 可重复读(repeatable-read):一个事务生成快照之后,可重复读取自己的快照信息,并不关心其他事务对数据修改的提交与否,但该事务执行写操作时,是以数据库真实数据进行操作的,而不是快照信息。
  4. 串行化(serializable):将所有事务进行排序,一个一个执行。

  四种事务隔离级别已给出,那么,这些隔离级别与并发事务问题的对应关系是怎样的呢?如下表所示:

隔离级别脏读不可重复读幻读
读未提交存在存在存在
读已提交解决存在存在
可重复读解决解决存在
串行化解决解决解决

  在MySQL中如何设置事务呢?如下所示:
  1、开启事务

begin;

  2、查询事务隔离级别

select @@tx_isolation;

  3、设置事务隔离级别

set tx_isolation = 'transaction_leve';

transaction_leve可以为:
a. read-uncommitted
b. read-committed
c. repeatable-read
d. serializable

  4、回滚事务

rollback

  5、提交事务

commit

五、总结

  本文主要介绍了MySQL事务的概念,用通俗易懂例子讲解了事务的特性,以及实现这些特性的原理。然后引出了并发情景下事务会产生的问题,最后围绕这些问题提出解决方法,也就事务的隔离级别。希望本文可以帮助到广大研友,对于文章中不对、不详细、不清楚的地方,请研友留言,本人一定会进行改善或作出回应。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值