文章目录
- 前言
- (一)原子性
- (二)持久性
- (三)隔离性
- (四)一致性
前言
在讨论MySQL事务四大特性的实现原理,需要先掌握事务与MySQL日志相关知识,可先看这两篇文章:
先来回顾下,事务的四大特性:
- 原子性(Atomicity):事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,是事务最核心的特性,事务本身就是以原子性来定义的。
- 持久性(Durability) :一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。当事务提交之后,数据会持久化到硬盘,修改是永久性的。
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 一致性(Consistency):一个事务必须使数据库从一个一致性状态变换到另一个一致性状态。
- 首先回顾一下一致性的定义。所谓一致性,指的是数据处于一种有意义的状态,这种状态是语义上的而不是语法上的。最常见的例子是转帐。例如从帐户A转一笔钱到帐户B上,如果帐户A上的钱减少了,而帐户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。
- 从这段话的理解来看,所谓一致性,即,从实际的业务逻辑上来说,最终结果是对的、是跟程序员的所期望的结果完全符合的
然后先简单的说下四大特性相关的原理:
- 原子性:实现主要基于undo log日志(回滚日志)。当事务回滚时,MySQL会使用undo日志将修改的数据还原到事务开始之前的状态。
- 持久性:实现主要基于redo log日志(重做日志)。每次事务提交时,MySQL会将修改的数据写入redo日志,以保证在数据库崩溃时,可以通过redo日志将数据重新恢复到提交前的状态。同时,MySQL还会定期将redo日志写入磁盘,以保证数据的持久性。
- 隔离性:InnoDB默认的隔离级别是RR,RR的实现主要基于锁机制、数据的隐藏列、undo log和类next-key lock机制。
- 一致性:事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障。它是上面三个特性都满足后的结果。
下面具体分析下。
(一)原子性
事务的原子性就如原子操作一般,表示事务不可再分,其中的操作要么都做,要么都不做。如果事务中一个SQL语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。事务的原子性表明事务就是一个整体,当事务无法成功执行的时候,需要将事务中已经执行过的语句全部回滚,使得数据库回归到最初未开始事务的状态。
事务的原子性就是通过undo log日志进行实现的。当事务需要进行回滚时,InnoDB引擎就会调用undo log日志进行SQL语句的撤销,实现数据的回滚。那么undo log是什么呢?
回滚日志(undo log)是InnoDB引擎提供的日志,顾名思义,回滚日志的作用就是对数据进行回滚。当事务对数据库进行修改,InnoDB引擎不仅会记录redo log(后面会讲),还会生成对应的undo log日志。如果事务执行失败或调用了rollback,导致事务需要回滚,就可以利用undo log中的信息将数据回滚到修改之前的样子。
但是undo log与redo log不一样,它属于逻辑日志。它对SQL语句执行相关的信息进行记录。当发生回滚时,InnoDB引擎会根据undo log日志中的记录做与之前相反的工作。比如对于每个数据插入操作(insert),回滚时会执行数据删除操作(delete);对于每个数据删除操作(delete),回滚时会执行数据插入操作(insert);对于每个数据更新操作(update),回滚时会执行一个相反的数据更新操作(update),把数据改回去。
undo log有两个作用,一是提供回滚,二是实现MVCC功能。
(二)持久性
事务的持久性是指当事务提交之后,数据库的改变就应该是永久性的,而不是暂时的。这也就是说,当事务提交之后,任何其它操作甚至是系统的宕机故障都不会对原来事务的执行结果产生影响。事务的持久性是通过InnoDB存储引擎中的redo log日志来实现的。
重做日志(redo log)是InnoDB引擎层的日志,用来记录事务操作引起数据的变化,记录的是数据页的物理修改,所以它是物理日志,记录内容是“在某个数据页上做了什么修改”。InnoDB引擎对数据的更新,是先将更新记录写入redo log日志,然后会在系统空闲的时候或者是按照设定的更新策略再将日志中的内容更新到磁盘之中。这就是所谓的预写式技术(Write Ahead logging)。这种技术可以大大减少IO操作的频率,提升数据刷新的效率。
关于redo log的详细解释,可看:MySQL三大日志(binlog、redo log和undo log)详解
(三)隔离性
原子性和持久性是单个事务本身层面的性质,而隔离性是指事务之间应该保持的关系。隔离性要求不同事务之间的影响是互不干扰的,一个事务的操作与其它事务是相互隔离的。由于事务可能并不只包含一条SQL语句,所以在事务的执行期间很有可能会有其它事务开始执行,因此多事务的并发性就要求事务之间的操作是相互隔离的。
事务之间的隔离,是通过锁机制实现的。当一个事务需要对数据库中的某行数据进行修改时,需要先给数据加锁。加了锁的数据,其它事务是不运行操作的,只能等待当前事务提交或回滚将锁释放。锁机制并不是一个陌生的概念,在许多场景中都会利用到不同实现的锁对数据进行保护和同步。而在MySQL中,根据不同的划分标准,还可将锁分为不同的种类。
- 按照粒度划分:行锁、表锁、页锁
- 按照使用方式划分:共享锁、排它锁
- 按照思想划分:悲观锁、乐观锁
具体关于MySQL中的锁,可看:数据库中的锁
关于事务问题和隔离级别,可看:MySQL事务【详解-最新的总结】
(四)一致性
一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。一致性是事务追求的最终目标,原子性、持久性和隔离性,实际上都是为了保证数据库状态的一致性而存在的。
换句话说,ACID里的AID都是数据库的特征,也就是依赖数据库的具体实现。而唯独这个C,实际上它依赖于应用层,也就是依赖于开发者,依赖于正确的逻辑。