浅析MySQL中的ACID实现

浅析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的内容被安全地刷到磁盘后,事务才能被认为是真正提交。

事务提交流程

  1. 事务开始时,分配新的事务ID并开始记录相应的undo和redo日志。

  2. 执行事务内的SQL语句,每一次修改都伴随生成undo日志,并将修改操作的redo信息写入redo log buffer。

  3. 当事务准备提交时,会先确保redo log buffer中的内容被刷新到磁盘,这个过程被称为"预写日志"(Write-Ahead Logging, WAL)策略。

  4. 确保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机制: 定期或按需将脏页刷回到磁盘,同步内存数据和磁盘数据,优化恢复过程。

相关推荐
苹果醋34 分钟前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
了一li27 分钟前
Qt中的QProcess与Boost.Interprocess:实现多进程编程
服务器·数据库·qt
码农君莫笑1 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
别致的影分身1 小时前
使用C语言连接MySQL
数据库·mysql
过过过呀Glik1 小时前
在 Ubuntu 上安装 MySQL 的详细指南
mysql·ubuntu
京东零售技术3 小时前
“慢”增长时代的企业数据体系建设:超越数据中台
数据库
sdaxue.com3 小时前
帝国CMS:如何去掉帝国CMS登录界面的认证码登录
数据库·github·网站·帝国cms·认证码
o(╥﹏╥)4 小时前
linux(ubuntu )卡死怎么强制重启
linux·数据库·ubuntu·系统安全
阿里嘎多学长4 小时前
docker怎么部署高斯数据库
运维·数据库·docker·容器
Yuan_o_4 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端