mysql之两阶段提交

什么是两阶段提交
当有数据修改时,会先将修改redo log cache和binlog cache然后在刷入到磁盘形成redo log file,当redo log file全都刷入到磁盘时(prepare 状态)和提交成功后才能将binlog cache刷入磁盘,当binlog全部刷新到磁盘后会记录一个xid,然后在relo log file上打上commit标志(commit阶段)。

为什么要有两阶段提交
MySQL在修改数据时,MySQL是先从磁盘中将数据copy到内存,然后再将内存中的数据进行修改,并记录redo log buffer 然后在通过系统调用将事务日志写入磁盘redo log file 最后最后事务提交后将内存中修改后的数据在开始写入磁盘中。
1.当只有redo log,binlog失效时,会导致主库可以通过redo log来重做,而从库因为没有及时获取到binlog而不能进行回放,导致主从数据不一致。这样会出现 redo log 写入到磁盘了,但是 binlog 还没写入磁盘,于是当发生 crash recovery 时,恢复后,主库会应用redo log,恢复数据,但是由于没有 binlog,从库就不会同步这些数据,主库比从库“新”,造成主从不一致
2.跟上一种情况类似,很容易知道,这样会反过来,造成从库比主库“新”,也会造成主从不一致。

而两阶段提交,就解决这个问题,crash recovery 时:
如果 redo log 已经 commit,那毫不犹豫的,把事务提交
如果 redo log 处于 prepare,则去判断事务对应的 binlog 是不是完整的
是,则把事务提交
否,则事务回滚
两阶段提交,其实是为了保证 redo log 和 binlog 的逻辑一致性。

 mysql的两阶段提交原理

(1) perpare阶段 写入redo日志
1、设置undo state=TRX_UNDO_PREPARED;
2、刷事务更新产生的redo日志;
(2) commit阶段 写入binlog日志
1、将事务产生的binlog写入文件,刷入磁盘;
2、设置undo页的状态,置为TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE; //标记可以清理回滚段
3、记录事务对应的binlog偏移,写入系统表空间。
两阶段提交是跨系统维持数据逻辑一致性时常用的一个方案。两阶段存在阻塞难题,提出的三阶段提交,在二阶段的基础上增加了一个预提交。
(3)假设法验证为什么分两阶段:
A. 先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。
但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。
B. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
(4)redo和binlog这两种日志有以下三点不同。
1、 redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
2、 redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
3、redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
(5) binlog组提交
引入队列机制保证innodb commit顺序与binlog落盘顺序一致,并将事务分组,组内的binlog刷盘动作交给一个事务进行,实现组提交目的。binlog提交将提交分为了3个阶段,FLUSH阶段,SYNC阶段和COMMIT阶段。每个阶段都有一个队列,每个队列有一个mutex保护,约定进入队列第一个线程为leader,其他线程为follower,所有事情交由leader去做,leader做完所有动作后,通知follower刷盘结束。binlog组提交基本流程如下:
FLUSH 阶段
(1) 持有Lock_log mutex [leader持有,follower等待]
(2) 获取队列中的一组binlog(队列中的所有事务)
(3) 将binlog buffer到I/O cache
(4) 通知dump线程dump binlog
SYNC阶段
(1) 释放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待
(2) 将一组binlog 落盘(sync动作,最耗时,假设sync_binlog为1
COMMIT阶段
(1) 释放Lock_sync mutex,持有Lock_commit mutex[leader持有,follower等待]
(2) 遍历队列中的事务,逐一进行innodb commit
(3) 释放Lock_commit mutex
(4) 唤醒队列中等待的线程
说明:由于有多个队列,每个队列各自有mutex保护,队列之间是顺序的,约定进入队列的一个线程为leader,因此FLUSH阶段的leader可能是SYNC阶段的follower,但是follower永远是follower。

MYSQL目前的组提交方式解决了一致性和性能的问题。通过二阶段提交解决一致性,通过redo log和binlog的组提交解决磁盘IO的性能。

猜你喜欢

转载自blog.csdn.net/jbossjf/article/details/126216851