一个逻辑事情单元要成为事务,在关系型数据库管理系统中,必须知足 4 个特性

原子性 : 事务的所有操作,要么全部完成,要么全部不完成,不会结束在某个中间环节同等性 : 事务开始之前和事务结束之后,数据库的完全性限定未被毁坏隔离性 : 数据库系统供应一定的隔离机制,担保事务在不受外部并发操作影响的“独立”环境实行。
这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然持久性 : 事务完成之后,它对付数据的修恰是永久性的,纵然涌现系统故障也能够保持

ACID 及它们之间的关系如下图所示,比如 4 个特性中有 3 个与 WAL 有关系,都须要通过 Redo、Undo 日志来担保等。

同等性

首先来看同等性,同等性实在包括两部分内容,分别是约束同等性和数据同等性。

phpmysql事务锁MySQL  解读MySQL事务与锁机制 Webpack

约束同等性:我们该当很随意马虎想到数据库中创建表构造时所指定的外键、Check、唯一索引等约束。
可惜在 MySQL 中,是不支持 Check 的,只支持其余两种,以是约束同等性就非常随意马虎理解了。
数据同等性:是一个综合性的规定,或者说是一个把握全局的规定。
由于它是由原子性、持久性、隔离性共同担保的结果,而不是单单依赖于某一种技能。
原子性

原子性便是前面提到的两个“要么”,即要么改了,要么没改。
也便是说用户感想熏染不到一个正在改的状态。
MySQL 是通过 WAL(Write Ahead Log)技能来实现这种效果的。

原子性和 WAL 到底有什么关系呢?

举例来讲,如果事务提交了,那改了的数据就生效了,如果此时 Buffer Pool 的脏页没有刷盘,如何来担保改了的数据生效呢?就须要利用 Redo 日志规复出来的数据。
而如果事务没有提交,且 Buffer Pool 的脏页被刷盘了,那这个本不应该存在的数据如何消逝呢?就须要通过 Undo 来实现了,Undo 又是通过 Redo 来担保的,以是终极原子性的担保还是靠 Redo 的 WAL 机制实现的。

隔离性

所谓隔离性,指的是一个事务的实行不能被其他事务滋扰,即一个事务内部的操作及利用的数据对其他的并发事务是隔离的。
锁和多版本掌握就符合隔离性。

持久性

所谓持久性,便是指一个事务一旦提交,它对数据库中数据的改变就该当是永久性的,接下来的操作或故障不应该对其有任何影响。
前面已经讲到,事务的原子性可以担保一个事务要么全实行,要么全不实行的特性,这可以从逻辑上担保用户看不到中间的状态。
但持久性是如何担保的呢?一旦事务提交,通过原子性,即便是碰着宕机,也可以从逻辑年夜将数据找回来后再次写入物理存储空间,这样就从逻辑和物理两个方面担保了数据不会丢失,即担保了数据库的持久性。

并发事务掌握单版本掌握-锁

锁用独占的办法来担保在只有一个版本的情形下事务之间相互隔离,以是锁可以理解为单版本掌握。

在 MySQL 事务中,锁的实现与隔离级别有关系,在 RR(Repeatable Read)隔离级别下,MySQL 为理解决幻读的问题,以捐躯并行度为代价,通过 Gap 锁来防止数据的写入,而这种锁,由于其并行度不足,冲突很多,常常会引起去世锁。

多版本掌握-MVCC

多版本掌握也叫作 MVCC,是指在数据库中,为了实现高并发的数据访问,对数据进行多版本处理,并通过事务的可见性来担保事务能看到自己该当看到的数据版本。

那个多版本是如何天生的呢?每一次对数据库的修正,都会在 Undo 日志中记录当前修正记录的事务号及修正前数据状态的存储地址(即 ROLL_PTR),以便在必要的时候可以回滚到老的数据版本。
例如,一个读事务查询到当前记录,而最新的事务还未提交,根据原子性,读事务看不到最新数据,但可以去回滚段中找到老版本的数据,这样就天生了多个版本。

多版本掌握很奥妙地将稀缺资源的独占互斥转换为并发,大大提高了数据库的吞吐量及读写性能。

原子性实现事理

每一个写事务,都会修正 Buffer Pool,从而产生相应的 Redo 日志,这些日志信息会被记录到 ib_logfiles 文件中。
由于 Redo 日志是遵照 Write Ahead Log 的办法写的,以是事务是顺序被记录的。

在 MySQL 中,任何 Buffer Pool 中的页被刷到磁盘之前,都会先写入到日志文件中,这样做有两方面的担保。

如果 Buffer Pool 中的这个页没有刷成功,此时数据库挂了,那在数据库再次启动之后,可以通过 Redo 日志将其规复出来,以担保脏页写下去的数据不会丢失,以是必须要担保 Redo 先写。
由于 Buffer Pool 的空间是有限的,要载入新页时,须要从 LRU 链表中淘汰一些页,而这些页必须要刷盘之后,才可以重新利用,那这时的刷盘,就须要担保对应的 LSN 的日志也要提前写到 ib_logfiles 中,如果没有写的话,适值这个事务又没有提交,数据库挂了,在数据库启动之后,这个事务就没法回滚了。
以是如果不写日志的话,这些数据对应的回滚日志可能就不存在,导致未提交的事务回滚不了,从而不能担保原子性,以是原子性便是通过 WAL 来担保的持久性实现事理

如下图所示,一个“提交”动作触发的操作有:binlog 落地、发送 binlog、存储引擎提交、flush_logs, check_point、事务提交标记等。
这些都是数据库担保其数据完全性、持久性的手段。

那这些操作如何做到持久性呢?前面讲过,通过原子性可以担保逻辑上的持久性,通过存储引擎的数据刷盘可以担保物理上的持久性。
这个过程与前面提到的 Redo 日志、事务状态、数据库规复、参数 innodb_flush_log_at_trx_commit 有关,还与 binlog 有关。
这里多提一句,在数据库规复时,如果创造某事务的状态为 Prepare,则会在 binlog 中找到对应的事务并将其在数据库中重新实行一遍,来担保数据库的持久性。

隔离性实现事理

InnoDB 支持的隔离性有 4 种,隔离性从低到高分别为:读未提交、读提交、可重复读、可串行化。

读未提交(RU,Read Uncommitted)。
它能读到一个事务的中间过程,违背了 ACID 特性,存在脏读的问题,以是基本不会用到,可以忽略。
读提交(RC,Read Committed)。
它表示如果其他事务已经提交,那么我们就可以看到,这也是一种最普遍适用的级别。
但由于一些历史缘故原由,可能 RC 在生产环境中用的并不多。
可重复读(RR,Repeatable Read),是目前被利用得最多的一种级别。
其特点是有 Gap 锁、目前还是默认的级别、在这种级别下会常常发生去世锁、低并发等问题。
可串行化,这种实现办法,实在已经并不是多版本了,又回到了单版本的状态,由于它所有的实现都是通过锁来实现的。

说到隔离性的实现办法,我们常日用 Read View 表示一个事务的可见性。
前面讲到 RC 级别的事务可见性比较高,它可以看到已提交的事务的所有修正。
而 RR 级别的事务,则没有这个功能,一个读事务中,不管其他事务对这些数据做了什么修正,以及是否提交,只要自己不提交,查询的数据结果就不会变。
这是如何做到的呢?

随着韶光的推移,读提交每一条读操作语句都会获取一次 Read View,每次更新之后,都会获取数据库中最新的事务提交状态,也就可以看到最新提交的事务了,即每条语句实行都会更新其可见性视图。
而反不雅观可重复读,这个可见性视图,只有在自己当前事务提交之后,才去更新,以是与其他事务是没有关系的。

在 RR 级别下,永劫光未提交的事务会影响数据库的 PURGE 操作,从而影响数据库的性能,以是可以对这样的事务添加一个监控。

可串行化是通过锁来实现的,以是实际上并不是多版本掌握,它的特点也很明显:读锁、单版本掌握、并发低。

同等性实现事理

同等性可以归纳为数据的完全性。
数据的完全性是通过其他三个特性来担保的,包括原子性、隔离性、持久性,而这三个特性,又是通过 Redo/Undo 来担保的 ,为了担保数据的完全性,提出来三个特性,这三个特性又是由同一个技能来实现的,以是理解 Redo/Undo 才能理解数据库的实质。

如上图所示,逻辑上的同等性,包括唯一索引、外键约束、check 约束,这属于业务逻辑范畴.