Mysql之事务

本文阅读 39 分钟
首页 代码,Java 正文

这里我们需要先解释一下,虽然我们的标题是Mysql之事务,但是我们需要清楚Mysql数据库本身是不支持事务的,而真正支持事务的是Mysql所使用的存储引擎(如InnoDB存储引擎),并且Mysql数据库所支持的众多存储引擎中支持事务的非常少,这里我们只讲解InnoDB存储引擎下的事务原理。

事务是数据库区别于文件系统的重要特性之一。在事务型数据库中,事务是访问并更新数据库中各项数据项的一个程序执行单元。在事务中的操作,要么都做修改,要么都不做。

理论上来说,事务有这非常严格的定义,它必须同时满足四个特性,如下:

  • 原子性 所谓原子性就是一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说不可能只执行其中一部分操作。
  • 一致性 数据库总是从一个一致性的状态转换到另外一个一致性的状态。
  • 隔离性 通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。注意这里说了“通常来说”,因为这种隔离性要取决于我们使用的隔离级别,某些隔离级别下,一个事务是可以看到其他事务没有提交的数据的,当然这也会出现“脏读”的问题。
  • 持久性 所谓持久性就是一旦事务提交,则其所作的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。但是持久性的概念有点模糊,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些未必。而且不可能有能做到100%的持久性保证的策略(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢?)。后续我们会继续讨论Mysql持久性的真正含义。

需要注意的是,任何事务机制在实现时,都应该考虑事务的ACID特性,这包括:本地事务分布式事务。即便分布式事务不能都很好的满足,也要考虑支持到什么程度。

从事务理论的角度来说,可以把事务分为以下几种类型:

  • 扁平事务
  • 带有保存点的扁平事务
  • 链事务
  • 嵌套事务
  • 分布式事务 对于InnoDB来说,其支持扁平事务、带有保存点的事务、链事务、分布式事务。

在了解mysql的事务隔离级别之前,我们先说一下为什么要有事务的隔离级别?我们知道Mysql的innodb引擎是支持事务的,当多个线程控制多个事务进行读写操作时就会出现一些意想不到的事情,主要有以下情况:

  • 脏读 一个事务可以读到其他事务没有提交的修改数据,这种现象被称为“脏读
  • 不可重复读 所谓不可重复读就是一个事务内执行两次查询可能得到不同的查询结果。
  • 幻读 所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围内的记录时,会产生幻行。

为了解决数据库中事务并发所产生的问题,在标准SQL规范中,定义了四种事务隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。

SQL规范的四种隔离级别:

  • READ UNCOMMITTED(未提交读) 此隔离级别可能会出现“脏读”,“不可重复读”,“幻读”等问题。从性能上来说,此级别没有别其他的级别好太多,却缺乏其他级别的很多好处,除非真的有非常必要额理由,否则实际中不要使用这种隔离级别。
  • READ COMMITTED(提交读) 大多数数据库的默认隔离级别都是READ COMMITTED,但是Mysql除外。此隔离级别可以解决脏读的问题,但有时会出现两次执行同样的查询,可能会得到不一样结果的问题,也就是不可重复读的问题。换句话说,此级别解决了脏读的问题,但是可能会出现不可重复读和幻读的问题。
  • REPETABLE READ(可重复读) REPETABLE READ是Mysql的默认隔离级别。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是此隔离级别却无法解决幻读的问题。换句话说,此隔离级别解决了“脏读”,“不可重复读的问题”,但是无法解决幻读的问题。InnoDB存储引擎通过多版本并发控制(MVCC)解决了幻读的问题。
  • SERIALIZABLE(可串行化) SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读问题,也就是说,此隔离级别解决“脏读”,“不可重复读”,“幻读”等所有问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际中很少使用,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑用该级别。

对于mysql的这四种隔离级别,我们可能有些疑问。

可不重复读和幻读有什么区别? 答:表面上来看两者非常的相似,主要的区别在于不可重复读倾向于对同一条数据的修改,而幻读则倾向于新增或删除数据。 如何保证 REPEATABLE READ 级别绝对不产生幻读? 答:在SQL中加入 for update (排他锁) 或 lock in share mode (共享锁)语句实现。就是锁住了可能造成幻读的数据,阻止数据的写入操作。

大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。

了解过MySQL事务的同学,就会知道,为了达成本地事务,MySQL做了很多的工作,比如回滚日志,重做日志,MVCC,读写锁等。

以MySQL5.6版本 的InnoDB 存储引擎为例,介绍一下单一数据库的事务实现原理。

InnoDB 是通过日志和锁来保证的事务的 ACID特性,具体如下:

  1. 通过数据库锁的机制,保障事务的隔离性;
  2. 通过 Redo Log(重做日志)来,保障事务的持久性;
  3. 通过 Undo Log (撤销日志)来,保障事务的原子性;
  4. 通过 Undo Log (撤销日志)来,保障事务的一致性;

Undo Log 如何保障事务的原子性呢? 具体的方式为:在操作任何数据之前,首先将数据备份到一个地方(这个存储数据备份的地方称为 Undo Log),然后进行数据的修改。如果出现了错误或者用户执行了 Rollback 语句,系统可以利用 Undo Log 中的备份将数据恢复到事务开始之前的状态。 Redo Log如何保障事务的持久性呢? 具体的方式为:Redo Log 记录的是新数据的备份(和 Undo Log 相反)。在事务提交前,只要将 Redo Log 持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是 Redo Log 已经持久化。系统可以根据 Redo Log 的内容,将所有数据恢复到崩溃之前的状态。

从以上描述可以看出,对于InnoDB存储引擎的事务实现来说,最重要的就是redo log和undo log,下面我们详细讲解redo log和undo log。

redo log

redo log被称为重做日志,通常是物理日志,记录的是页的物理修改操作(例如:偏移量10002修改为“aaa”)。其由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易丢失的;二是重做日志文件(redo log file),其是持久的。之所以要有重做日志缓冲(redo log buffer),原因很简单就是为了缓解内存操作和磁盘操作的速度差异。但是,由于重做日志缓冲(redo log buffer)中的内容在数据库出现故障时很容易丢失,所以要用重做日志文件进行持久化。大致过程为:在InnoDB中,当有事务操作时,会将事务中的操作记录到重做日志缓冲区,注意无论事务有没有提交都会不断生成重做日志记录,然后按一定的频率将重做日志缓冲区中的数据顺序的刷新到重做日志文件中。重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到重做日志文件

  • Master Thread每一秒将重做日志刷新到重做日志文件,无论事务有没有提交,都会进行刷新操作。
  • 每个事务提交时会将重做日志刷新到重做日志文件。
  • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件

从上面可以看出,无论满足上述哪种情况将重做日志缓冲刷新到重做日志文件时,都需要调用fsync操作。需要注意的是,重做日志缓冲是存取引擎中的内存区域,而重做日志文件是Mysql所在操作系统上的文件,所以在进行刷新操作时,其实是先将重做日志缓冲写入到文件系统缓冲,然后再进行fsync操作写入到重做日志文件。由于fsync的效率取决于磁盘的性能,一次磁盘的性能决定了事务的提交的性能,也就是数据库的性能。 InnoDB存储引擎允许用户手工设置非持久性的情况发生,一次提高数据库的性能。参数innodb_flush_log_at_trx_commit用来控制重做日志刷新到磁盘的策略。该参数取值如下:

  • 1:表示事务提交时必须调用一次fsync操作。默认为此值。
  • 0:表示事务提交时不进行写入重做日志操作,这个操作仅在master thread中完成,而在master thread中每秒会进行一次重做日志文件的fsync操作
  • 2:表示事务提交时将重做日志缓冲写入重做日志文件,但仅写入文件系统的缓冲中,不进行fsync操作。在这种情况下,当Mysql数据库发生宕机而操作系统不发生宕机时,并不会导致事务的丢失。而当操作系统宕机时,重启数据后会丢失未从文件系统缓存刷新到重做日志文件的那部分事务。 至于哪种设置好,需要我们根据具体的业务在应用性能和数据安全之间做权衡来决定。

log block

在InnoDB存储引擎中,重做日志都是以512字节进行存储的。这意味着重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,称之为重做日志快(redo log block),每块的大小为512字节。换句话说,从重做日志缓存和重做日志文件都是由每个为512kb大小的日志块所组成。 若一个页中产生的重做日志数量大于512kb,那么需要分割为多个重做日志块进行存储。此外,由于重做日志块的大小和磁盘扇区大小一样,都是512kb,因此重做日志的写入可以保证原子性,不需要doublewrite技术。

log group

log group为重做日志组,其中有多个重做日志文件。 但是在ha_innobase.cc文件中禁用了此功能。因此InnoDB存储引擎实际只有一个log group。

log group是一个逻辑上的概念,并没有一个实际存储的物理文件来表示log group信息。log group由多个重做日志文件组成,每个log group中的日志文件大小都是相同的。

重做日志文件中存储的就是之前在log buffer中保存的log block,因此其也是根据块的方式进行物理存储的管理,每个块的大小和log block一样,都为512字节。

对于log block的写入追加在redo log file的最后部分,当一个redo log file被写满时,会接着写入下一个redo log file,其使用方式为round-robin。

虽然log block总是在redo log file的最后部分进行写入,但是对redo log file的写入并不是顺序的。因为redo log file除了保存log buffer刷新到磁盘的log block,还保存了一些其他的信息,这些信息一共占用2KB大小,即每个redo log file的前2KB的部分不保存log block的信息。对于log group中的第一个redo log file,其前2KB的部分保存4个512字节大小的块,分别是:log file header、checkpoint1、空、checkpoint2。log group中的其余redo log file仅保留这些空间,但不保存上述信息。正因为保存了这些信息,这意味着redo log file的写入并不是完全顺序的,因为其除了log block的写操作,还需要更新前2KB的信息,这些信息对于InnoDB存储引擎的恢复操作来说非常关键和重要。

在log file header后面的部分为InnoDB存储引擎保存的checkpoint值,其设计是交替写入,这样的设计避免了因介质失败而导致无法找到可用的checkpoint的情况。

重做日志的格式由已下三部分组成:

  • redo_log_type:重做日志的类型
  • space:表空间ID
  • page_no:也的偏移量

LSN(日志序列号)

在InnoDB存储引擎中,LSN占用8字节,并且单调递增。LSN表示的含义有:

  • 重做日志写入的总量
  • checkpoint的位置
  • 页的版本 LSN表示事务写入重做日志的字节的总量。当然,LSN不仅记录在重做日志中,还存在于每个页中。在页中,LSN表示该页最后刷新时LSN的大小。因为重做日志记录的是每个页的日志,因此页中的LSN用来判断页是否需要进行恢复操作。

可以通过SHOW ENGINE INNODB STATUS查看LSN的情况,其中有三个值很重要:

  • Log sequence number:表示当前的LSN
  • Log flushed up to:表示刷新到重做日志文件的LSN
  • Last checkpoint at:表示刷新到磁盘的LSN

恢复

InnoDB存储引擎在启动时不管上次数据运行时是否正常关闭,都会尝试进行恢复操作。因为重做日志记录的是物理日志,因此恢复的速度比逻辑日志要快的多。与此同时,InnoDB存储引擎自身也对恢复进行了一定程度的优化,如顺序读取及并行应用重做日志,这样可以进一步地提高数据库恢复的速度。 因为checkpoint表示已经刷新到磁盘页上的LSN,因此在恢复过程中仅需恢复checkpoint开始的日志部分。

undo log

重做日志记录了事务的行为,可以很好的童工其对也进行“重做”操作。但是事务有时需要进行回滚啊哦做,这时就需要undo。因此在对数据库进行修改时,InnoDB存储疫情不但会产生redo,还会产生一定量的undo。这样如果用户执行的事务或者语句由于某种原因失败了,或者执行了回滚语句,就可以利用这些undo信息将数据回滚到修改之前的样子。 redo存放在重做日志文件中,但undo存放在数据库内部的一个特殊段中,这个段成为undo段**。undo段位于共享表空间内。可以通过py_innodb_page__info.py工具查看当前共享表空间中undo的数量。

不同于redo ,undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子,所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。这是因为在多用户并发系统中,可能会有成百上千事务。数据库的主要任务就是协调对数据记录的并发访问。比如,一个事务在修改当前一个页中的某几条记录,同时还有别的事务对同一页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这会影响到其他事务正在进行的工作。

当InnoDB存储引擎回滚时,它实际上做的是与先前相反的工作。对于每个insert,InnoDB存储引擎会完成一个delete;对于每个delete,InnoDB存储疫情会执行一个inert;对于每一个update,会执行一个相反的update,将修改前的行放回去。

undo的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成的。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读。

最后也是最重要的一点,undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。

undo存储管理

InnoDB存储引擎对undo的管理同样采用段的方式。但是这个段和上面介绍的端有所不同。首先InnoDB存储引擎有rollback segment,每个回滚段记录了1024个undo log segment,而在每个undo log segment中进行页的申请。共享表空间偏移量为5的页记录了所有rollback segment header所在的页。

InnoDB1.2版本最大支持128个rollback segment,也就是支持同时在线的事务限制为128*1024,并可通过参数对rollback segment做进一步的设置。这些参数包括:

  • innodb_undo_directory:设置rollback segment文件所在的路径。这意味着可以rollback segment可以存放在共享表空间以外的位置。默认值为“.”,表示当前InnoDB存储引擎的目录。
  • innodb_undo_logs:设置rollback segment的个数,默认为128。需要注意的是该参数用来替换之前版本的参数innodb_rollback_segments。
  • innodb_undo_tablespaces:设置构成rollback segment文件的数量,这样rollback segment可以较为平均地分布在多个文件中。设置该参数后,会在路径innodb_undo_directory看到undo为前缀的文件,如:undo001,undo002。

需要特别注意的是,事务在undo log segment分配页并写入undo log的这个过程中同样需要写入重做日志。当事务提交时,InnoDB存储引擎会做以下两件事情:

  • 将undo log放入列表中,以供之后purge操作。
  • 判断undo log所在的页是否可以重用,若可以分配则给下个事务使用 事务提交后并不能马上删除undo log以及undo log所在的页。这是因为可能还有其他事务需要童工undo log来得到记录之前的版本。故事务提交时将undo放入一个链表中,是否可以最终删除undo log 及undo log所在页由purge线程来判断。

另外,InnoDB不会为每个事务单独的分配undo页,因为这样 会浪费存储空间。因此,在InnoDB存储引擎的设计中对undo页可以进行重用。具体来说,当事务提交时,首先将undo log放入链表中,然后判断undo页的使用空间是否小于3/4,若是则表示该undo页可以被重用,之后新的undo log记录在当前undo log的后面。由于存放undo log的列表是以记录进行组织的,而undo页可能存放着不同事务的undo log,因此purge操作需要涉及磁盘的离散读取操作,是一个比较缓慢的过程。

undo log 格式

在Innodb存储引擎中,undo log分为:

  • insert undo log:指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见,因此undo log可以在事务提交后直接删除。不需要进行purge操作。
  • update undo log:记录的是对delete和updte操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。 无论是insert undo log还是update undo lo它们的格式有一部分是一样的。开始的前两个字节next记录的是下一个undo log的位置,通过next的字节可以知道一个undo log所占的空间字节数。类似地,尾部的两个字节记录的是undo log的开始位置。type_cmpl占用一个字节,记录的是undo log的类型。undo_no记录耳朵是事务的ID,table_id记录undo log所对应的表对象。undo_no和table_id都是在压缩后保存的。接着的部分记录了所有主键的列和值。在进行rollback操作时,根据这些值可以定位到具体的记录,然后进行删除即可。

purge

和我们想象不一样的是,delete和update操作可能并不直接删除原有的数据。例如,表t中列a有聚集索引,列b有辅助索引,执行如下SQL语句: delect from t where a=1; 对于上述的delete操作,其实仅是将主键列等于1的记录delete flag设置为1,记录并没有被删除,即记录还是存在于B+树中。而真正删除这行记录的操作其实被“延迟”了,最终在purge操作中完成。 purge 用来完成最终delete和udpate操作。这种设计是因为InnoDB存储引擎支持MVCC,所以记录不能在事务提交时立即进行处理。这时其他事务可能正在引用这行,故InnoDB存储引擎需要保存记录之前的版本。而是否可以删除该条记录通过purge来进行判断。若改行记录已不被任何其他事务引用,那么就可以进行真正的delete操作。可见,purge操作是清理之前的delete和update 操作,将上述操作“最终”完成。而实际执行的操作为delete操作,清理之前行记录的版本。

上面介绍过,为了节省空间,InnoDB存储引擎的undo log设计是这样的:一个页上允许多个事务的undo log存在。虽然这不代表事务在全局过程中的提交顺序,但是后面的事务产生的undo log总在最后。此外,InnoDB存储引擎还有一个history列表,它根据事务提交的顺序,将undo log进行链接。

history list表示按照事务提交的顺序将undo log进行组织。需要特别注意,在InnoDB存储疫情的设计中,先提交的事务总在尾端。undo page存放了undo log,由于可以重用,因此一个undo page中可能放了多个不同事务的undo log。

在执行purge的过程中,InnoDB存储引擎首先从history list中找到第一个需要被清理的记录(一般是尾端的记录),清理之后InnoDB存储引擎会在此事务的undo log所在的页中继续寻找是否存在可以被清理的记录,如果没有可被清理的记录,则再次去history list中查找最尾端的记录,然后依次再把对应事务的记录进行清理。当某个undo page中所有的事务都被清理了,此undo page就可以被重用。 InnoDB存储引擎这种先从histroy list中找undo log,然后在从undo page中找undo log的设计模式是为了避免大量的随机读取操作,从而提供purge的效率。

group commit

若事务为非只读事务,则每次事务提交时需要进行一次fsync操作,以确保重做日志都写入磁盘。当数据库发生宕机时,可以通过重做日志进行恢复。由于fsync操作的性能是有限的,为了提高磁盘fsync的效率,当前数据库都提供了group commit的功能,即一次fsync可以刷新确保多个事务日志被写入文件。对于InnoDB存储引擎来说,事务提交时会进行两阶段的操作:

  • 修改内存中事务对应的信息,并且将日志写入重做日志缓冲
  • 调用fsync将确保日志都从重做日志缓冲写入磁盘

需要注意的是,在InnoDB1.2版本之前,在开启二进制日志后,InnoDB存储引擎的group commit功能会失效,从而导致性能降低。为什么会导致这个问题呢?原因是:在开启二进制日志后,为了保证存储引擎层中的事务和二进制日志的一致性,二者之间使用了两阶段事务:

  1. 当事务提交时InnoDB存储引擎进行prepare操作
  2. Mysql数据库上层写入二进制日志
  3. InnoDB存储引擎层将日志写入重做日志文件。 a: 修改内存中事务对应的信息,并且将日志写入重做日志缓冲。 b:调用fsync将确保日志都从重做日志缓冲写入磁盘 一旦步骤2中的操作完成,就确保了事务的提交,即使在执行步骤3时数据库发生了宕机。此外需要注意,每个步骤都需要进行一次fsync操作才能确保上下两层数据的一致性

为了保证Mysql数据库上层二进制日志的写入顺序和InnoDB层的事务提交顺序一致,Mysql数据库内部使用了prepare_commit_mutex这个锁。但是在启用这个锁之后,步骤3中的步骤a不可以在其他事务执行步骤b时进行,从而导致了group commit失效。 然而,为什么需要保证Mysql数据库上层二进制日志的写入顺序和InnoDB层事务提交顺序一致呢? 这是因为备份及恢复的需要。 不过,在Mysql5.6中使用BLGC方案解决了这个问题:不但实现了Mysql数据库层的二进制写入是group_commit的,而且InnoDB存储引擎层也是group commit的。此外还移除了原先的prepare_commit_mutex锁,从而大大提高了数据库的整体性能。

Mysql5.6中BLGC的实现方式是将事务提交的过程分几个步骤完成: Mysql数据库上层进行提交时首先按顺序将其放入一个队列中,队列中的第一个事务成为leader,其他事务成为follower,leader控制这follower的行为。BLGC的步骤分为以下三个阶段:

  • Flush阶段,将每个事务的二进制日志写入内存中
  • Sync阶段,将内存中的二进制日志刷新到磁盘,若队列中有多个事务,那么仅一次fsync操作就完成了二进制日志的写入,这就是BLGC。
  • commit阶段,leader根据顺序调用存储引擎层事务的提交,InnoDB存储引擎本就支持group commit,因此修复了原先由于锁prepare_commit_mutex导致的group commit失效的问题。 当有一组事务进行commit阶段时,其他新事务可以进行Flush阶段,从而是group commit不断生效。当然group commit的效果由队列中事务的数量决定,若每次队列中仅有一个事务,那么可能效果和之前差不多,甚至会更差。但当提交的事务越多时,group commit的效果越明显,数据库性能的提升也就越大。 参数binlog_max_flush__queue_time用来控制Flush阶段中等待的时间,即使之前的一组事务完成提交,当前一组的事务也不马上进入Sync阶段,而是至少需要等待一段时间。这样做的好处是group commit的事务数量更多,然而这也可能会导致事务的响应时间变慢。该参数默认值为0,且推荐设置依然为0。除非用户的Mysql数据库系统中有着大量的连接,并且不断的在进行事务的写入或更新操作。

InnoDB存储引擎提供了对XA事务的支持,并通过XA事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的ACID要求又有了提高。另外,在使用分布式事务时,InnoDB存储引擎的事务隔离级别必须设置为SERIALIZABLE。

XA事务允许不同类型数据库之间的分布式事务,如一台是Mysql,另一台是oracle,又可能有一台是SQl server,只要参与在全局事务中的每个节点都支持XA事务。 XA事务由一个或多个资源管理器、一个事务管理器以及一个应用程序组成。

  • 资源管理器:提供访问事务资源的方法。通常一个数据库就是一个资源管理器。
  • 事务管理器:协调参与全局事务中的各个事务。需要和参与全局事务的所有资源管理器进行通信。
  • 应用程序:定义事务的边界,指定全局事务中的操作。 在Mysql数据库得了分布式事务中,资源管理器就是Mysql数据库,事务管理器为连接Mysql服务器的客户端。

分布式事务使用两阶段提交的方式。在第一阶段,所有参与全局事务的节点都开始准备,告诉事务管理器它们准备好提交了。在第二阶段,事务管理器告诉资源管理器执行commit或rollback。但是由于两阶段提交会有一些问题,后来又出现了三阶段提交,详情会在“ 分布式事务简介”这篇文章中介绍。

在单个节点中运行分布式事务没有太大的实际意义,但是要在Mysql数据库的命令下演示多个节点参与的分布式事务也是行不通的。通常来说,都是通过编程语言来完成分布式事务的操作的。当前Java的JTA可以很好的支持Mysql的分布式事务,需要使用分布式事务应该认真参考其API。

Mysql内部XA事务

上面讨论的分布式事务是外部事务,即资源管理器是Mysql数据库本身。在Mysql数据库中还存在另外一种分布式事务,其在存储引擎与插件之间,又或者在存储引擎与存储引擎之间,称之为内部XA事务。 最为常见的内部XA事务存在于binlog与InnoDB存储引擎之间。由于复制的需要,因此目前绝大多数的数据库都开启了binlog功能。在事务提交时,先写二进制日志,再写InnoDB存储引擎的重做日志。对上述两个操作的要求也是原子的,即二进制日志和重做日志必须同时写入。若二进制日志先写入,而在写入InnoDB存储引擎InnoDB存储引擎时发生了宕机,那么slave可能会接收到master传过去的二进制日志并执行,最终导致了主从不一致的情况。

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://blog.csdn.net/qq_38571892/article/details/119760526
-- 展开阅读全文 --
Web安全—逻辑越权漏洞(BAC)
« 上一篇 03-13
Redis底层数据结构--简单动态字符串
下一篇 » 04-10

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复