两阶段提交
在MySQL数据库中,特别是使用InnoDB存储引擎时,两阶段提交机制用于协调redo-log和bin-log的写入,以确保数据的一致性和持久性。redo-log是InnoDB特有的日志,用于在系统崩溃时恢复事务,而bin-log是MySQL的服务器层日志,用于数据备份、主从复制。
bin-log(二进制日志)
bin-log是MySQL数据库中的一种日志类型,它以二进制格式记录了所有对数据库执行更改的操作,包括插入(INSERT)、更新(UPDATE)和删除(DELETE)等。
bin-log记录了数据库的所有更改操作,但不包括SELECT查询或其他只读操作。每个操作都会被记录下来,包括操作的语句、执行操作的时间戳和执行操作的线程ID。这些信息足以在另一个数据库实例上重新执行,以复制原始数据库的状态。
作用:
- 备份与恢复: bin-log可以用于数据库的备份和恢复,通过重新执行日志中的操作来恢复数据库到某个特定时间点的状态。
- 主从复制: 在MySQL的主从复制架构中,bin-log是实现数据同步的关键。从服务器(slave)通过读取主服务器(master)的bin-log,并在本地执行这些操作,从而保持主从数据的一致性。
redo-log(重做日志)
redo-log是MySQL中InnoDB存储引擎特有的一种日志,它确保了事务的持久性。每当数据库发生修改时,InnoDB会首先将这些修改记录到redo-log中。
redo-log记录了事务对数据文件的物理修改操作。这些操作包括页面的修改(例如,哪一页被修改了,哪些数据被更新了)、事务的ID、事务的状态(如提交或回滚)等信息。redo-log是物理日志,它记录的是对数据文件的低级修改,而不是SQL语句。
作用:
- 事务持久性: redo-log保证了即使在数据库修改数据页之前发生系统崩溃,也能通过redo-log恢复未提交的事务。当系统重启后,InnoDB会根据redo-log中的记录来重新应用这些修改,确保数据的持久性和一致性。
一阶段提交的问题
如果只进行一次日志写入,即不使用两阶段提交(two-phase commit,2PC),那么可能会出现数据不一致的问题:
- 先写bin-log,再写redo-log
- 场景:主机在事务提交时首先写入bin-log,然后准备写入redo-log。
- 问题:如果在写入redo-log之前发生故障(例如,断电导致主机宕机),那么主机重启后不会恢复该事务的数据,因为redo-log中没有记录。但是,由于bin-log已经写入,从机可能已经同步了这一事务,导致从机比主机多出一条数据。
- 先写redo-log,再写bin-log
- 场景:主机在事务提交时首先写入redo-log,然后准备写入bin-log。
- 问题:如果在写入bin-log之前发生故障,主机重启后会根据redo-log恢复数据,因为redo-log保证了事务的持久性。然而,从机依赖于bin-log来同步数据,由于bin-log中没有该事务的记录,从机不会同步这一事务,导致从机比主机少一条数据。
两阶段提交的过程
两阶段提交的过程涉及将重做日志(redo log)的写入拆分为两个步骤:准备(prepare)阶段和提交(commit)阶段,并在其间穿插写入二进制日志(binlog)。
- 准备阶段(Prepare Phase)
- 写入Redo Log的Prepare状态: 事务开始时,首先将事务的ID(XID)和相关的更改记录到Redo Log中。此时,事务的状态被标记为“prepare”。这一步确保了如果系统故障,事务有足够的信息可以恢复。
- 持久化Redo Log: Redo Log被写入到磁盘上,这一步是通过设置
innodb_flush_log_at_trx_commit = 1
来确保的,意味着每次事务提交时,Redo Log都会被强制刷新到磁盘。
- 提交阶段(Commit Phase)
- 写入Binlog: 接下来,事务的XID被写入到Binlog中。Binlog是MySQL服务器用于记录更改历史的日志,用于数据复制和恢复。
- 持久化Binlog: Binlog随后被刷新到磁盘,这是通过设置
sync_binlog = 1
来保证的,这样保证了Binlog的可靠性。 - Redo Log状态更新为Commit: 一旦Binlog写入磁盘成功,InnoDB存储引擎就会被告知事务可以提交了。此时,Redo Log中的事务状态从“prepare”更新为“commit”。这个状态的更新不需要立即刷新到磁盘,因为如果系统崩溃,可以根据Binlog来恢复。
两阶段提交(two-phase commit)机制确保了redo-log和bin-log之间的一致性,即使在崩溃的情况下也能保持数据的正确同步。两阶段提交过程中有三个潜在的崩溃点,以及相应的恢复策略:
- redo-log(prepare) 崩溃点:
- 过程: 事务在写入准备状态的redo记录后崩溃。
- 恢复策略: 由于事务还未提交,崩溃不会影响数据一致性。重启后,系统会根据redo-log中的信息判断事务未完成,并进行相应的回滚操作。
- bin-log 崩溃点:
- 过程: 事务在写入bin-log记录时崩溃。
- 恢复策略: 重启后,系统会检查redo-log中的事务状态。如果发现事务已准备好但未提交,且bin-log中没有对应的记录,系统会回滚该事务。
- redo-log(commit) 崩溃点:
- 过程: 事务在bin-log写入成功后,但在写入redo-log的提交记录时崩溃。
- 恢复策略: 由于bin-log已经记录了事务,从机可以同步这些数据。重启时,系统会检测到事务已准备好且bin-log中有记录,因此会重新写入redo-log的提交记录,以确保事务的提交。
只要redo-log写入了prepare状态且bin-log成功写入,事务即被视为成功。在这之前发生的崩溃则视为事务失败。redo-log写入commit状态的步骤确保了即使在写入bin-log时发生崩溃,事务也不会仅根据redo-log的prepare状态而提交。