KILL 無效pstack `pidof mysqld` > hang.info pt-pmp hang.info # 統計線程分布
線程分類:
innodb_thread_concurrency=16 限制)用戶線程 → 需 log_sys->mutex → 寫 redo log buffer
↓
將髒頁加入 flush_list 需 log_sys->log_flush_order_mutex
↓
log_flush_order_mutex 被佔用 → 所有需 log_sys->mutex 線程阻塞
↓
srv_error_monitor_thread 也需 log_sys->mutex → 無法執行死鎖檢測/喚醒
↓
整個實例 Hang
log_sys->log_flush_order_mutex = {waiters=1, lock_word=0}mutex_exit_func → sync_arr_wake_threads_if_sema_free 喚醒等待者// 源碼註釋關鍵揭示 // Due to memory barriers, the order of mutex_get_waiters() and mutex_reset_lock_word() // may be reversed during execution, causing hang. // Special function sync_arr_wake_threads_if_sema_free() written to mitigate this.
執行順序:
log_flush_order_mutexwaiters=1)mutex_reset_lock_word() 先於 mutex_get_waiters() 執行(CPU 重排序)sync_arr_wake_threads_if_sema_free 判斷「鎖空閒且無等待者」→ 不喚醒log_sys->mutex,阻塞所有後續線程log_sys->mutex,等待 log_flush_order_mutex(未被喚醒)log_sys->mutex 阻塞sync_arr_wake_threads_if_sema_free 修復 memory barrier 導致的假死// 原代碼:log_get_lsn() 直接獲取 log_sys->mutex 讀取 LSN
// 修復:改為 log_peek_lsn() 嘗試非阻塞獲取
lsn = log_peek_lsn(); // 內部 mutex_enter_nowait() 失敗即返回,不阻塞
// log_peek_lsn 實現
ulint log_peek_lsn(void) {
if (mutex_enter_nowait(&log_sys->mutex)) return log_sys->lsn;
mutex_exit(&log_sys->mutex);
return 0; // 獲取失敗返回 0,調用方容忍
}
核心思想:避免在死鎖檢測/監控路徑上獲取熱點鎖,寧可讀不到最新 LSN 也不阻塞。
innodb_thread_concurrency、降低並發壓測強度、避免長事務log_sys->mutex、log_flush_order_mutex 等核心鎖等待情況sync_arr_wake_threads_if_sema_free 模式此案例是 MySQL 內核並發控制經典教材,展示了「微小的 CPU 重排序 → 鎖喚醒失效 → 監控線程阻塞 → 全實例 Hang」的多米諾效應。讀懂源碼註釋,往往能直達問題本質。