首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 組提交深度解析:Redo Log 組提交、兩階段提交、Binlog 組提交三階段演進與 5.6 核心源碼邏輯

     編輯:MySQL 綜合教學

MySQL 組提交深度解析

組提交 是 MySQL 解決「提交時頻繁 fsync 刷盤」性能瓶頸的核心優化。從早期僅支援 Redo Log 組提交,到 5.6 正式解決 **Binlog 組提交** 與 **兩階段提交 (2PC)** 一致性難題,本文結合源碼邏輯圖解三階段演進全貌。

階段一:Redo Log 組提交 (WAL 基礎優化)

  • 背景: WAL 機制要求事務提交前將 Redo Log 刷盤 (fsync),單事務提交 = 一次 fsync,高併發下 I/O 瓶頸嚴重。
  • 原理: 多事務並發提交時,**後到的事務「搭便車」**:事務 T3 刷盤時,將 T1、T2 的 Redo Log (LSN 範圍連續) 一同刷入磁碟,一次 fsync 完成多事務持久化。
  • 關鍵點: LSN (Log Sequence Number) 單調遞增,天然保證順序;log_sys->mutex 保護拷貝緩衝區;flushed_to_disk_lsn 標記已刷盤位置。

階段二:兩階段提交 (2PC) — 解決 Binlog 與 Redo Log 一致性

  • 問題: 主從複制依賴 Binlog,崩潰恢複依賴 Redo Log,**兩者順序必須完全一致**,否則主從數據不一致。
  • 流程:
Prepare 階段:
  1. InnoDB 刷 Redo Log (含 Prepare 標記)
  2. 釋放行鎖,回滾段標記 Prepared

Commit 階段:
  1. InnoDB 寫 Commit 標記,釋放所有鎖
  2. Binlog 寫入事務事件 (順序與 Prepare 一致)
  3. InnoDB 更新事務狀態為 Committed
  • 故障恢複邏輯: 重啟掃描 Prepared 事務 → 若 Binlog 存在 → Commit;否則 Rollback。
  • 舊瓶頸: 為保證順序,引入 **prepare_commit_mutex** 將 Prepare/Commit **串行化**,導致組提交失效,性能大幅下降。

階段三:Binlog 組提交 (MySQL 5.6 突破) — 隊列化 + 管道化

核心思想:**引入三階段隊列**,將事務分組,組內 Binlog 刷盤由 Leader 完成,Follower 等待通知,實現「無鎖管道化」組提交。

三階段隊列架構

階段隊列鎖Leader 職責Follower 行為
FLUSHflush_queueLOCK_log收集隊列事務 → Binlog 寫入 IO Cache → 通知 Dump 線程等待 Leader 完成
SYNCsync_queueLOCK_syncfsync Binlog 到磁碟 (最耗時,sync_binlog=1)等待 Leader 完成
COMMITcommit_queueLOCK_commit遍歷隊列 → 調用 InnoDB commit (釋放鎖、寫 Commit 標記)等待 Leader 完成 → 被喚醒返回

關鍵協議:Leader/Follower 角色傳遞

  • 事務進入 FLUSH 隊列時,**第一個進入者成為 Leader**,其餘為 Follower。
  • Leader 完成 FLUSH → 釋放 LOCK_log → 進入 SYNC 隊列,**可能成為 Follower** (若已有其他事務在 SYNC)。
  • **Follower 永遠是 Follower**,不搶鎖、不干活、只等待條件變量通知。
  • 這種**管道化**設計讓 FLUSH、SYNC、COMMIT 三階段可**並行流水線**執行,極大提升吞吐。

源碼關鍵結構 (簡化)

// 事務提交入口
MYSQL_BIN_LOG::commit(THD *thd, bool all) {
    // 1. 進入 FLUSH 階段
    enter_stage(&flush_queue, &LOCK_log, &cond_flush);
    if (am_leader) flush_stage();  // 寫 IO Cache
    wait_for_stage_done();

    // 2. 進入 SYNC 階段
    enter_stage(&sync_queue, &LOCK_sync, &cond_sync);
    if (am_leader) sync_stage();   // fsync
    wait_for_stage_done();

    // 3. 進入 COMMIT 階段
    enter_stage(&commit_queue, &LOCK_commit, &cond_commit);
    if (am_leader) commit_stage(); // InnoDB commit 循環
    wakeup_followers();
}

效能提升數據 (官方 Benchmark)

  • 啟用 Binlog 且 sync_binlog=1 時:5.6 組提交 **QPS 提升 3-5 倍**,延遲降低 80%+ (vs 5.5 串行化)。
  • 配合 **GTID + 多線程從庫 (MTS)** 進一步釋放從庫重放並發度。

避坑與最佳實踐

  • ⚠️ sync_binlog=1 最安全 (每提交必 fsync),但壓力最大;sync_binlog=N (N>1) 可批量 fsync,權衡性能與風險 (崩潰丟失 N 事務)。
  • ⚠️ **大事務殺手**:單事務產生巨量 Binlog 會阻塞整個隊列管道,**嚴禁大事務**,業務拆分為小事務。
  • ✅ 監控 Binlog_commits vs Binlog_group_commits 比值,評估組提交效果。

結語: 組提交是 MySQL 從「單機寫入優化」邁向「高併發分布式一致性」的基石。**5.6 隊列化管道化設計** 优雅地解決了 2PC 與組提交的看似矛盾,成為後續 MariaDB、Percona、AliSQL 等分支繼續優化的藍圖。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved