組提交 是 MySQL 解決「提交時頻繁 fsync 刷盤」性能瓶頸的核心優化。從早期僅支援 Redo Log 組提交,到 5.6 正式解決 **Binlog 組提交** 與 **兩階段提交 (2PC)** 一致性難題,本文結合源碼邏輯圖解三階段演進全貌。
log_sys->mutex 保護拷貝緩衝區;flushed_to_disk_lsn 標記已刷盤位置。Prepare 階段:
1. InnoDB 刷 Redo Log (含 Prepare 標記)
2. 釋放行鎖,回滾段標記 Prepared
Commit 階段:
1. InnoDB 寫 Commit 標記,釋放所有鎖
2. Binlog 寫入事務事件 (順序與 Prepare 一致)
3. InnoDB 更新事務狀態為 Committed
核心思想:**引入三階段隊列**,將事務分組,組內 Binlog 刷盤由 Leader 完成,Follower 等待通知,實現「無鎖管道化」組提交。
| 階段 | 隊列 | 鎖 | Leader 職責 | Follower 行為 |
|---|---|---|---|---|
| FLUSH | flush_queue | LOCK_log | 收集隊列事務 → Binlog 寫入 IO Cache → 通知 Dump 線程 | 等待 Leader 完成 |
| SYNC | sync_queue | LOCK_sync | fsync Binlog 到磁碟 (最耗時,sync_binlog=1) | 等待 Leader 完成 |
| COMMIT | commit_queue | LOCK_commit | 遍歷隊列 → 調用 InnoDB commit (釋放鎖、寫 Commit 標記) | 等待 Leader 完成 → 被喚醒返回 |
// 事務提交入口
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();
}
sync_binlog=1 最安全 (每提交必 fsync),但壓力最大;sync_binlog=N (N>1) 可批量 fsync,權衡性能與風險 (崩潰丟失 N 事務)。Binlog_commits vs Binlog_group_commits 比值,評估組提交效果。結語: 組提交是 MySQL 從「單機寫入優化」邁向「高併發分布式一致性」的基石。**5.6 隊列化管道化設計** 优雅地解決了 2PC 與組提交的看似矛盾,成為後續 MariaDB、Percona、AliSQL 等分支繼續優化的藍圖。