MySQL InnoDB 核心模組中,Purge 線程、IO 線程、用戶工作線程並發操作 Insert Buffer (IBUF) 時,特定執行序列會觸發 UNABLE TO PURGE A RECORD 錯誤。因涉及多線程競態,隨機難以復現。本文詳述如何使用 MySQL 內建除錯機制 DEBUG_SYNC 與 DBUG_EXECUTE_IF 實現穩定復現,為修復提供可驗證基準。
IBUF_OP_INSERT (插入)、IBUF_OP_DELETE_MARK (刪除標記)、IBUF_OP_DELETE (Purge 實際刪除)DELETE_MARK → INSERT → DELETE 特定交錯順序MySQL 原始碼巨集,透過訊號機制實現線程間精確同步。
// 用戶線程執行 INSERT (產生 IBUF_OP_INSERT)
row_ins_step() {
// ... 程式碼 A ...
DEBUG_SYNC("after_user_insert_ibuf");
// ... 程式碼 B ...
}
// Purge 線程執行 DELETE (產生 IBUF_OP_DELETE)
purge_step() {
// ... 程式碼 a ...
DEBUG_SYNC("after_purge_delete_ibuf");
// ... 程式碼 b ...
}
-- Session 1 (用戶線程)
SET DEBUG_SYNC = 'after_user_insert_ibuf SIGNAL user_insert_done WAIT_FOR purge_delete_done';
-- Session 2 (Purge 線程,需透過 debug 介面或注入)
SET DEBUG_SYNC = 'after_purge_delete_ibuf SIGNAL purge_delete_done WAIT_FOR user_insert_done';
優點:精確控制執行順序;缺點:需修改原始碼重編譯、難以控制後台線程 (Purge/IO) 的 Session 設定。
在關鍵路徑植入條件式睡眠,透過全域 debug 變數啟用。
// storage/innobase/ibuf/ibuf0ibuf.cc - ibuf_insert()
ibuf_insert(..., ibuf_op_t op, ...) {
// 強制 Purge 的 DELETE 操作等待 3 秒
DBUG_EXECUTE_IF("sleep_ibuf_insert_delete", {
if (op == IBUF_OP_DELETE) my_sleep(3000000); // 微秒
});
ibuf_insert_low(...);
}
// storage/innobase/ibuf/ibuf0ibuf.cc - ibuf_merge_pages()
ibuf_merge_pages(...) {
// 強制 Merge 操作等待 5 秒 (比 Purge 更久)
DBUG_EXECUTE_IF("sleep_ibuf_merge", {
my_sleep(5000000);
});
buf_read_ibuf_merge_pages(...);
}
SET GLOBAL debug = '+d,sleep_ibuf_insert_delete,sleep_ibuf_merge';
-- 1. 設定小 Buffer Pool,掃描大表驅逐目標頁
SET GLOBAL innodb_buffer_pool_size = 16M;
SELECT * FROM large_table; -- 驅逐目標頁
-- 2. Session 1: 執行 DELETE (產生 DELETE_MARK)
DELETE FROM target_table WHERE id = 1;
-- 3. Session 2: 稍後執行 INSERT 同頁 (產生 INSERT)
INSERT INTO target_table (id, ...) VALUES (1, ...);
-- 4. 等待 Purge 線程自然運行 (產生 DELETE)
-- 因 sleep_ibuf_insert_delete 延遲 Purge 的 DELETE
-- 因 sleep_ibuf_merge 延遲 Merge
-- 即可穩定獲得 DELETE_MARK → INSERT → DELETE 序列
innodb_buffer_pool_size + 全表掃描驅逐innodb_print_ddl_logs、GDB 斷點觀察關鍵變數參考資源: MySQL 官方除錯文件、何登成 MySQL 內核分析、GPFeng 博客。