浅析MySQL中的ACID实现
MySQL的InnoDB存储引擎通过多种底层机制来实现 ACID(Atomicity, Consistency, Isolation, Durability) 事务特性,这些机制构成了事务处理的基石,因此在这里做一个汇总分析。
一、原子性 (Atomicity)
原子性保证事务中的所有操作要么全部成功,要么全部失败。通过结合redo log和undo log的机制,InnoDB存储引擎能够确保在任何情况下,事务要么全部成功完成,要么完全回滚,不会出现部分成功的情况,从而实现了事务的原子性。
Undo Log(回滚日志)
当一个事务对数据库进行修改时(如INSERT、UPDATE或DELETE操作),InnoDB首先会产生对应的Undo Log记录。
Undo Log记录了原始数据的版本以及执行逆向操作所需的足够的信息,以便在需要回滚事务时恢复到事务开始前的状态。
Redo Log(重做日志)
在事务执行过程中,对数据页所做的所有更改都会先写入内存中的redo log buffer。
当事务提交时,并非立即更新磁盘上的实际数据文件,而是将redo log buffer中的内容刷新到磁盘上的redo log文件(ib_logfile*)中。
Redo Log确保即使在系统崩溃的情况下,也能通过重放这些日志来恢复未完成的事务更改,从而保证事务的持久性(在下面持久性中也会重复讲到),而这也是原子性实现的一部分。当redo log的内容被安全地刷到磁盘后,事务才能被认为是真正提交。
事务提交流程
-
事务开始时,分配新的事务ID并开始记录相应的undo和redo日志。
-
执行事务内的SQL语句,每一次修改都伴随生成undo日志,并将修改操作的redo信息写入redo log buffer。
-
当事务准备提交时,会先确保redo log buffer中的内容被刷新到磁盘,这个过程被称为“预写日志”(Write-Ahead Logging, WAL)策略。
-
确保redo log已经落盘之后,事务才会被标记为已提交状态,此时undo日志可以在新的事务开始之前被清理掉(对于已经提交的数据,不再需要保留undo日志)。
异常处理与回滚
如果事务在执行过程中遇到错误或者手动调用ROLLBACK,InnoDB会使用undo log来回滚已做的更改,撤销事务的影响。
回滚操作按照undo log中的逆序逐步执行,直到数据恢复到事务开始前的状态。
二、一致性 (Consistency)
一致性保证了数据库总是在符合特定业务规则的状态下。这主要是由应用层的逻辑以及数据库约束和触发器共同维护的。InnoDB存储引擎可以在并发环境下有效保障事务处理前后数据的一致性,确保数据库从一个一致状态转换到另一个一致状态。
ACID中的其他属性支持一致性
原子性保证了事务要么全部执行成功,要么全部回滚,避免了数据处于中间状态的问题。
隔离性通过不同的隔离级别(如读未提交、读已提交、可重复读和串行化)防止并发事务间的相互影响,使得每个事务在自己的视图中看到的数据是一致的。
事务隔离级别与一致性读
在可重复读隔离级别下,InnoDB使用多版本并发控制(MVCC)技术。每个事务看到的是一个快照,即事务开始时数据库的状态,这样就避免了脏读和不可重复读问题,有助于保持数据一致性。
约束检查
InnoDB在事务执行过程中以及提交前会进行各种完整性约束检查,包括但不限于:主键约束(Primary Key)、唯一约束(Unique Key)、外键约束(Foreign Key)、检查约束(Check Constraints)等,这些约束能够确保数据满足预定义的业务规则。
触发器和存储过程
MySQL还支持触发器(Triggers)和存储过程(Stored Procedures),它们能够在事务执行的关键点执行特定逻辑,以确保业务规则得到遵守,从而维护数据一致性。
两阶段锁定(2PL)
InnoDB采用悲观锁策略,在事务执行期间对要修改的数据加锁,并且在事务结束前不释放这些锁,直到事务提交或回滚。这有助于防止多个事务同时修改同一数据导致的一致性问题。
三、隔离性 (Isolation)
隔离性防止不同事务之间的相互干扰。InnoDB能够在保证数据正确性和并发性能的同时,有效实现不同事务间的隔离,以满足不同业务场景下的需求。
事务隔离级别
读未提交(Read Uncommitted): 一个事务可以读取到其他事务尚未提交的修改。可能会发生脏读,即事务读取到了随后可能被回滚的数据。
读已提交(Read Committed): 一个事务只能读取到已经提交的数据。避免了脏读,但在同一个事务内多次执行相同的查询可能会得到不同的结果,即发生了不可重复读现象。
可重复读(Repeatable Read): MySQL默认的事务隔离级别。事务在整个执行过程中看到的数据视图是一致的,对于同一事务内多次执行的相同查询语句,其结果总是相同的。
串行化(Serializable): 事务按照顺序依次执行,完全避免了脏读、不可重复读和幻读等问题。
隔离性实现
锁机制
InnoDB支持行级锁定,当一个事务修改一行数据时,会对这行数据加锁。根据不同的SQL操作和事务隔离级别,可能会使用共享锁(读锁)、排他锁(写锁)或意向锁等不同类型的锁。
在可串行化(Serializable)隔离级别下,虽然不是直接采用行级锁,但InnoDB会更严格地控制事务之间的并发访问,以避免幻读现象。
多版本并发控制(MVCC)
在“读已提交”(Read Committed)和“可重复读”(Repeatable Read)隔离级别下,InnoDB使用MVCC来提供非锁定读操作,使得在大部分情况下读操作不需要获取任何锁。
MVCC通过对每一行记录保存多个版本,并为每个事务分配一个唯一的事务ID(称为事务视图),使得事务可以查看到自己开始时的数据快照,从而避免了脏读、不可重复读的问题。
Next-Key Locks
在“可重复读”隔离级别下,为了防止幻读问题,InnoDB对索引记录和间隙同时进行锁定,这种锁定被称为Next-Key Locks。它不仅锁定当前行,还锁定该行前后的间隙,确保在同一事务中多次执行相同查询的结果一致。
一致性非锁定读
在可重复读隔离级别下,对于只读事务,InnoDB允许事务读取其他事务已经提交的更改,而不会阻塞这些事务的提交,这就是“一致性非锁定读”。
四、持久性 (Durability)
持久性确保一旦事务成功提交,其对数据库所做的更改将会一直存在,即便遇到硬件故障、系统崩溃等情况也能在数据库重启后得到恢复。
Redo Log(重做日志)
-
Redo Log是保证持久性的核心机制。在事务执行过程中,对数据库所做的更改首先会被记录到内存中的redo log buffer中。
-
当事务提交时,并非立即更新磁盘上的数据文件,而是将redo log buffer的内容刷新到磁盘上的redo log文件(ib_logfile*)中。这个过程被称为“预写日志”(Write-Ahead Logging, WAL)策略,即先写日志再修改数据。
-
确保redo log被安全地刷入磁盘后,事务才会被认为已经提交。这样即使在系统崩溃的情况下,也能通过重放这些redo log恢复尚未写入数据文件的事务操作。
Double Write Buffer
为了防止在操作系统缓存或I/O子系统层次发生故障导致数据页损坏,新写入的数据页首先被复制到一个连续的物理区域(double write buffer),然后才写入实际的数据文件,从而提高了数据页的恢复能力
Checkpoint机制
定期或者在某些特定条件触发下,InnoDB会将脏页(已被修改但未写回磁盘的数据页)从缓冲池刷回到磁盘上的数据文件,以减少恢复时间并释放内存资源。
事务提交流程
在事务提交时,InnoDB会确保redo log的持久化(flush至磁盘),然后再提交事务。这一顺序确保了即使在服务器突然宕机的情况下,也能通过redo log进行恢复,使得已提交的事务更改永久保存。
五、技术总结
在MySQL InnoDB存储引擎中,通过各种底层技术和策略的有效结合,得以在支持高并发处理的同时,保证事务的ACID特性,从而确保了数据库操作的可靠性和数据完整性。
日志系统
Redo Log(重做日志): 用于保证事务的持久性和原子性,记录了修改数据库的所有操作,并确保在事务提交前将这些更改先持久化到磁盘。
Undo Log(回滚日志): 用于保证事务的原子性和一致性,记录了数据旧版本以及撤销事务所需的信息,在事务回滚时用来恢复数据。
并发控制机制
锁机制: 通过行级锁、表级锁、意向锁等不同类型的锁控制并发事务对同一资源的访问,以实现隔离性。
多版本并发控制(MVCC): 在特定的隔离级别下,如“读已提交”和“可重复读”,避免锁定读取,提供非锁定读,从而提高并发性能并保持一定程度的一致性和隔离性。
事务管理与状态跟踪
事务开始时分配事务ID,并在事务执行过程中跟踪其状态及影响的数据变化。
为每个事务创建一个视图,使得事务能够看到自己启动时数据库的状态,以此来维持一致性和实现不同的隔离级别。
故障恢复机制
Double Write Buffer: 防止数据页损坏而无法恢复的情况。
Checkpoint机制: 定期或按需将脏页刷回到磁盘,同步内存数据和磁盘数据,优化恢复过程。
标签:事务,log,InnoDB,提交,MySQL,ACID,redo,浅析,隔离 From: https://blog.csdn.net/qq_45902224/article/details/136757573