两阶段提交

二阶段提交(2PC)是一种用于分布式数据库或系统中确保事务原子性的协议,它通过一个协调者和多个参与者共同协作,保证所有节点要么全部提交事务,要么全部回滚。

它的执行分为两个阶段:

  1. 投票阶段:协调者向所有参与者发送“准备提交”请求。参与者执行事务操作,并锁定资源,然后向协调者反馈“同意”或“中止”。
  2. 提交阶段:如果协调者收到所有参与者的“同意”,则发送“提交”命令,参与者完成提交并释放资源;如果任何参与者反馈“中止”或超时,则协调者发送“回滚”命令,所有参与者进行回滚。

undo log

MVCC

MVCC依靠undo log实现

回滚

记录阶段: “事务修改数据前,InnoDB会先将旧数据拷贝到Undo Log中,形成一条版本记录。不同操作记录不同内容:

  • UPDATE记录旧值

  • INSERT记录主键

  • DELETE记录完整数据”

构建版本链: “每条记录都有DB_ROLL_PTR指针,指向它的上一个版本。修改时,当前记录指向新的Undo Log,形成版本链”

回滚执行: “执行ROLLBACK时,==从后向前==遍历事务的所有Undo Log:

  • UPDATE:用旧值覆盖新值

  • INSERT:删除插入的记录

  • DELETE:清除删除标记,恢复数据 回滚完成后,这些Undo Log会被清理”

redo log

核心作用是确保事务的持久性(Durability),防止在发生故障时数据丢失。==它记录的是物理日志,即“在某个数据页上做了什么修改”==

工作流程:

事务修改数据:当一个事务对某行数据进行修改(如UPDATE)时,InnoDB会先将该数据所在的数据页从磁盘加载到内存的Buffer Pool中(如果尚未在内存中)。

生成Redo Log记录:在内存中修改数据页的同时,会生成一条对应的redo log记录。这条记录包含了“在哪个表空间的哪个数据页的哪个偏移量处,将数据改成了什么值”等信息。

写入Log Buffer:生成的redo log记录首先被写入内存中的Log Buffer(日志缓冲区)。==合并多次内存写操作,减少系统调用次数==

写入OS Cache:当事务提交时(默认设置下),根据一定的策略(如innodb_flush_log_at_trx_commit参数控制),Log Buffer中的内容会被写入到OS Cache的redo log文件(通常是ib_logfile0ib_logfile1)。注意,这个“写入”通常只是写到了操作系统的文件系统缓存(OS Cache)中,并没有真正“落盘”。==合并多次磁盘写操作,减少物理I/O次数==

刷盘(fsync):操作系统会在合适的时机(或根据数据库的刷盘策略)将OS Cache中的数据真正同步到物理磁盘上。只有完成这一步,修改才算拥有了持久化的保证。


前面是redo log日志流程↑,后面是具体的数据流程↓

后台刷脏页:InnoDB有后台线程,会定期或不定期地将Buffer Pool中已被修改的“脏页”刷新到磁盘的数据文件中。即使这个刷盘动作在事务提交后很久才发生,或者发生故障导致丢失,因为redo log已经持久化,数据也不会丢失

循环写入与检查点:Redo log文件是固定大小、循环写入的。它由两个或多个文件组成。当写满一个文件时,会切换到下一个文件继续写。当最后一个文件也被写满时,会回到第一个文件开头覆盖写入。为了避免覆盖掉还未将对应脏页刷盘的有效日志,InnoDB引入了检查点(Checkpoint)。检查点标记了在此之前的redo log对应的脏页都已经刷回磁盘,这部分日志空间就可以被安全地覆盖重用。

bin log

Binlog是MySQL Server层记录的逻辑日志,==记录的是SQL语句的原始逻辑或行变更前后的值==,其核心作用与Redo Log(存储引擎层物理日志)形成互补

第1步:事务执行与日志生成

  • 事务执行过程中,其修改数据的原始逻辑(取决于Binlog格式)会被记录到每个线程各自的 Binlog Cache 内存区域中。
  • 这个缓存是每个连接线程独享的,目的是不影响其他事务。

第2步:事务提交与日志写入

  • 事务提交时,该事务在Binlog Cache中的完整日志会被一次性写入到操作系统的文件系统缓存中,这个文件就是Binlog文件(如binlog.000001)。
  • 此时,对于MySQL Server来说,事务已经可以返回提交成功。但数据是否安全,取决于下一步。

第3步:日志刷盘(持久化)

  • 这是由核心参数 sync_binlog 控制的:
    • **sync_binlog = 0**:MySQL不主动调用fsync(),依赖操作系统定期将缓存刷盘。性能最好,但若系统崩溃,可能丢失多个事务的Binlog。
    • sync_binlog = 1默认且最安全。每次事务提交后,立即调用fsync()将Binlog强制刷入物理磁盘。这保证了只要事务提交成功,其Binlog就一定不会丢失。
    • **sync_binlog = N (N>1)**:每累计N个事务提交,才调用一次fsync()进行刷盘。这是性能与安全的折中,若系统崩溃,最多丢失N个事务的Binlog。

bin实习主从复制

  1. 主库提交事务,写入binlog
  2. 主库开线程同步,从库开线程接收binlog到relay log
  3. 从库开线程回放读取relay log,回放binlo

一般一主两从【因为同步需要开销】

redo 和 bin的两阶段提交

准备阶段:redo log写磁盘,但还未真正提交

提交阶段:binlog写磁盘,redo log中写入提交记录,事务真正提交

【保证redo和bin的一致性】