原创 详解MySQL事务原理

发布时间:2021-08-02 02:39:36 浏览 346 来源:猿笔记 作者:努力的老刘

    2;31:它实现事务的持久性即当事务提交时必须先将该事务的所有日志写入到重做日志文件进行持久化然后事务的提交操作完成才算完成为了确保每次日志都写入到重做日志文件在每次将重做日志缓冲写入重做日志后必须调用一次fsync操作(操作系统)将缓冲文件从文件系统缓存中真正写入磁盘

  • 老刘是即将找工作的研究生,自学大数据开发,一路走来,感慨颇深,网上大数据的资料良莠不齐,于是想写一份详细的大数据开发指南。这份指南把大数据的【基础知识】【框架分析】【源码理解】都用自己的话描述出来,让伙伴自学从此不求人。

  • 您的点赞是我持续更新的动力,禁止白嫖,看了就要有收获,一起加油。

今天给大家分享的是大数据开发基础部分MySQL的事务,事务在MySQL知识点中非常重要的部分,很多伙伴只是知道MySQL的四大特性,但不知道其中的原理,老刘这次给大家详细的描述MySQL四大特性的原理,MySQL事务篇的大纲如下:

什么是事务?

在MySQL中的事务是由存储引擎实现的,而且支持事务的存储引擎不多,我们主要讲解InnoDB存储引擎中的事务。

事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行。

事务用来管理DDL、DML、DCL操作,比如insert,update,delete语句,默认是自动提交的。

事务的四大特性(ACID)

  • Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部成功,要么全部失败。
  • Consistency(一致性):数据库在事务执行前后状态都必须是稳定的或者是一致的,就是说事务开始和结束后,数据库的完整性不会被破坏。
  • Isolation(隔离性):事务之间不会相互影响。由锁机制和MVCC机制来实现的,其中MVCC(多版本并发控制):优化读写性能(读不加锁、读写不冲突),四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE(串行化)。
  • Durability(持久性):事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。
  • 事务的使用

    begin或starttransaction:开启一个事务;

    commit:提交一个事务,并使已对数据库进行的所有修改称为永久性的;

    rollback:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

    ACID实现原理

    下面我们就来详细讲解一下上述示例涉及的事务的ACID特性的具体实现原理。总结来说,事务的隔离性由多版本控制机制和锁实现,而原子性、一致性和持久性通过InnoDB的redolog、undolog和ForceLogatCommit机制来实现。

    重做日志RedoLog

    如果要存储数据则先存储数据的日志,一旦内存崩了,则可以从日志找重做日志保证了数据的可靠性,InnoDB采用了WriteAheadLog(预写日志)策略,即当事务提交时,先写重做日志,然后再择时将脏页写入磁盘。如果发生宕机导致数据丢失,就通过重做日志进行数据恢复。

    回滚日志UndoLog

    数据库崩溃重启后需要从redolog中把未落盘的脏页数据恢复出来,重新写入磁盘,保证用户的数据不丢失。当然,在崩溃恢复中还需要回滚没有提交的事务。由于回滚操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志来保证,所以崩溃恢复先做redo恢复数据,然后做undo回滚。

    所以,在事务执行的过程中,除了记录redolog,还会记录一定量的undolog。undolog记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undolog进行回滚操作。

    ForceLogatCommit机制

    \ u 003 CDATA-tool = " mdnice editor " style = " font-size:16px;padding-top:8px;填充-底部:8px边距:0;行高:26px颜色:黑色>它实现事务持久化,即事务提交时,事务的所有日志必须写入重做日志文件进行持久化,然后完成事务的提交操作。为了确保每个日志都写入重做日志文件,在每个重做日志缓冲区写入重做日志后,必须调用一次fsync操作(操作系统)才能将缓冲区文件从文件系统缓存写入磁盘。

    总结一下就是redolog用于在崩溃时恢复数据,undolog用于对事务的影响进行撤销,也可以用于多版本控制。而ForceLogatCommit机制保证事务提交后redolog日志都已经持久化。

    原子性

    原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。例如银行转账要么成功,要么失败,是不存在中间的状态!

    UndoLog是实现原子性的关键,靠的就是undolog。当事务对数据库进行修改时,InnoDB会生成对应的undolog。undolog它属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undolog的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。

    以update操作为例:当事务执行update时,其生成的undolog中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。

    持久性

    持久性是指事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。

    InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(BufferPool),BufferPool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从BufferPool中读取,如果BufferPool中没有,则从磁盘读取后放入BufferPool;当向数据库写入数据时,会首先写入BufferPool,BufferPool中修改的数据会定期刷新到磁盘中。

    虽然BufferPool的使用大大提高了读写数据的效率,但是也有别的问题,当MySQL宕机,而此时BufferPool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

    于是,优秀的程序员们引入了redolog,当我们对数据进行修改时,除了修改BufferPool中的数据,还会在redolog中记录这次操作。当事务提交时,会调用fsync接口对redolog进行刷盘。如果MySQL宕机,重启时可以读取redolog中的数据,对数据库进行恢复。

    还有一点必须知道就是redolog采用的是WAL策略,所有修改先写入日志,再更新到BufferPool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。

    隔离性

    在MySQL隔离性中,一般有两种情况:

  • 要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。
  • 在进行读操作的时候,可能出现脏读、不可重复读、幻读的问题。
  • 首先讲第一种情况,MySQL要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。

    锁机制的基本原理可以理解为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。

    至于锁机制中的锁,一般就是之前讲到的MySQL锁,大家可以去看看这篇MySQL锁的内容。

    接着讲第二种情况,读操作可能出现脏读、不可重复读、幻读的问题。

    隔离性追求的是并发情形下事务之间互不干扰,但是在事务的并发操作中可能会出现一些问题:

  • 丢失更新:两个事务针对同一数据都发生修改操作时,会存在丢失更新问题。
  • 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段。之后,若T2回滚,T1读取的内容就是临时且无效的。
  • 不可重复读:对于两个事务T1,T2,T1读取了一个字段,然后T2更新了该字段。之后,T1再次读取同一个字段,发现字段的内容不一样。要求,多次读取数据的时候,在一个事务中读出的都应该是一样的。一般是由于update操作引发,所以将来执行的时候要特别注意。
  • 幻读:对于两个事务T1,T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行。之后。如果T1再次读取同一个表,就会多出几行。就是发现数据的数量不一样。要求,在一个事务中多次去读取数据的时候都应该是一样的。
  • 虽然有上述这些问题,但MySQL数据库为我们提供的四种隔离级别(由低到高):

  • Readuncommitted(读未提交):最低级别,任何情况都无法保证。
  • Readcommitted(RC,读已提交):可避免脏读的发生。
  • Repeatableread(RR,可重复读):可避免脏读、不可重复读的发生。(InnoDB默认级别为RR,它可以解决幻读,主要原因是Next-Key(Gap)锁,只有RR才能使用Next-Key锁)
  • Serializable(串行化):可避免脏读、不可重复读、幻读的发生。
  • 解决脏读、不可重复读、幻读的问题使用的是MVCC,即多版本的并发控制协议。它说的就是在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。

    MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列(也可以称之为标记位)和undolog。其中数据的隐藏列包括了该行数据的版本号、删除时间、指向undolog的指针等等;当读取数据时,MySQL可以通过隐藏列判断是否需要回滚并找到回滚需要的undolog,从而实现MVCC。

    MVCC如何解决脏读、不可重复读、幻读的问题

    1、MVCC解决脏读

    当事务T1在第三个时刻读取自己的余额时,会发现数据已被T2事务修改,并且T2的状态还没有提交。此时事务A读取最新数据后,根据数据的undolog执行回滚操作,得到事务T2修改前的数据,从而避免了脏读。

    2、MVCC解决不可重复读

    \ u 003 CDATA-tool = " mdnice editor " style = " font-size:16px;padding-top:8px;填充-底部:8px边距:0;行高:26px颜色:黑色>交易T1第二次第一次读取数据时,会记录数据的版本号(数据的版本号记录在第行),假设版本号为1;当交易T2修改并提交自己的余额时,该银行记录的版本号增加,假设版本号为2;当事务T1第五次再次读取数据时,发现数据的版本号2大于第一次读取时记录的版本号1,因此将根据撤销日志进行回滚操作,以获得版本号为1的数据,从而实现可重复读取。

    3、MVCC解决幻读

    InnoDB实现的RR通过next-keylock机制避免了幻读现象。

    next-keylock是行锁的一种,实现相当于recordlock(记录锁)+gaplock(间隙锁),它的特点是不仅会锁住记录本身(recordlock的功能),还会锁定一个范围(gaplock的功能)。

    当事务T1在第二个时刻第一次读取0

    稍微总结下,InnoDB通过锁机制、数据的隐藏列、undolog和类next-keylock,实现了一定程度的隔离性,可以满足大多数场景的需要。不过需要说明的是,RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离。

    一致性

    一致性是事物追求的最终目标,前面提到的原子性,隔离性,持久性都是为了保证数据库的一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。

    总结

    本文作为大数据开发指南MySQL的第四篇详细介绍了MySQL事务的内容,尤其是MySQL四大特性的原理。希望大家能够跟着老刘的文章,好好捋捋思路,争取能够用自己的话把这些知识点讲述出来!

    尽管当前水平可能不及各位大佬,但老刘会努力变得更加优秀,让各位小伙伴自学从此不求人!

    大数据开发指南地址如下:

  • github:

    作者信息

    努力的老刘 [等级:3] 自学不求人的帅小伙
    发布了 24 篇专栏 · 获得点赞 106 · 获得阅读 5966
  • 相关推荐 更多