掘金 后端 ( ) • 2024-06-21 18:45

theme: z-blue

最近在学习seata相关知识,会陆续的写一系列博客记录自己的学习,这篇博客先来过一下基础理论知识,方便后续的学习。

二阶段提交

为什么

二阶段提交是分布式事务的解决方案

分布式事务:

需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。

应用场景:跨库事务、分库分表

是什么

两阶段提交(Two-Phase Commit,简称 2PC)是一种分布式事务协议,确保所有参与者在提交或回滚事务时都处于一致的状态。2PC 协议包含以下两个阶段:

阶段1:

TM(事务管理器)通知各个RM(资源管理器)准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就对工作内容进行持久化(修改前的原始数据记录到事务日志中,确保事务的可恢复性),再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。

阶段2:

TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。

缺点

  • 同步阻塞问题

2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放。

  • 单点故障

由于协调者的重要性,一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

  • 数据不一致

若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一部分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题。

mysql应用

前置mysql日志知识回顾:

undo log:保证事务原子性,形成版本链实现MVCC多版本并发控制

redo log:重做日志,即在数据库崩溃的时候,可通过redo log恢复数据(InnoDB引擎独有),环形数组的形式进行循环写

binlog:以二进制格式追加存储的归档日志,它属于server层(与什么存储引擎无关),并且是逻辑日志,主要记录数据的变更操作,包括DDL和DML语言,但不包括数据查询(SELECT、SHOW)语句,主要用于数据备份、恢复、主从复制

mysql中的二阶段:因为 MySQL 把 binlog 也看作一个存储引擎,开启 binlog,SQL 语句改变(插入、更新、删除)InnoDB 表的数据,这个 SQL 语句执行过程中,就涉及到两个存储引擎。

使用二阶段提交,就是为了保证两个存储引擎的数据一致性。

为什么要保证binlog与redo log一致性

MySQL在修改数据时,MySQL是先从磁盘中将数据copy到内存,然后再将内存中的数据进行修改,并记录redo log buffer 然后在通过系统调用将事务日志写入磁盘redo log file(持久化), 最后事务提交后将内存中修改后的数据在开始写入磁盘中。

当redo log,binlog不一致时,如果发生宕机,主库恢复数据可以通过redo log,而从库是需要通过binlog进行主从复制,这会导致主从数据不一致。

场景

用户事务提交分为两种场景,如果开启了 binlog,它们都会使用二阶段提交。

场景 1:通过 BEGIN 或其它开始事务的语句,显式开始一个事务,用户手动执行 COMMIT 语句提交事务。

场景 2:没有显式开始的事务,一条 SQL 语句执行时,InnoDB 会隐式开始一个事务,SQL 语句执行完成之后,自动提交事务。

如果没有开启 binlog,SQL 语句改变表中数据,不产生 binlog,不用保证 binlog 和表中数据的一致性,用户事务也就不需要使用二阶段提交了。

原理

perpare阶段 写入redo日志
1、设置undo state=TRX_UNDO_PREPARED;
2、刷事务更新产生的redo日志;
commit阶段 写入binlog日志
1、将事务产生的binlog写入文件,刷入磁盘;
2、设置undo页的状态,置为TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE; //标记可以清理回滚段
3、记录事务对应的binlog偏移,写入系统表空间。