磁碟故障、掉電、殺進程、OOM Killer 導致 MySQL 異常關閉,重啟時卡在 InnoDB: Doing recovery... 階段最終報 mysqld got signal 11 (Segmentation fault) 退出。此時數據頁、Redo Log、Undo Log 可能已損壞,正常崩潰恢復失效。innodb_force_recovery 是最後的「核武器級」搶救手段 — **跳過損壞頁檢查、跳過事務回滾、跳過 Redo Log 前滾**,強行拉起實例導出數據重建。本文記錄一次真實生產搶救全過程。
InnoDB: Doing recovery: scanned up to log sequence number 9120034833
150125 16:12:51 InnoDB: Starting an apply batch of log records to the database...
InnoDB: Progress in percents: 5 6 7 ... 40
150125 16:12:51 [ERROR] mysqld got signal 11 ; -- Segfault,恢復過程中訪問非法記憶體
分析:Redo Log 在應用階段遇到損壞頁/邏輯錯誤觸發 Segfault,屬於 **嚴重元數據/數據頁損壞**,常規恢復無效。
在 [mysqld] 區段設置 innodb_force_recovery = N,級別包含關係 (N 級包含 N-1 級所有行為):
| 級別 | 常量名 | 行為 | 風險 |
|---|---|---|---|
| 1 | SRV_FORCE_IGNORE_CORRUPT | 忽略檢測到的損壞頁,繼續啟動 | 可能讀取到髒數據 |
| 2 | SRV_FORCE_NO_BACKGROUND | 阻止 Master Thread 運行 (阻止 Full Purge、Merge 等後台操作) | 無法清理 Undo Log,空間增長 |
| 3 | SRV_FORCE_NO_TRX_UNDO | 不執行事務回滾 (視未提交事務為已提交) | 數據不一致風險極高 |
| 4 | SRV_FORCE_NO_IBUF_MERGE | 不合併 Insert Buffer | 二級索引可能缺失記錄 |
| 5 | SRV_FORCE_NO_UNDO_LOG_SCAN | 不掃描 Undo Log,視未提交事務為已提交 | 同級別 3,更徹底 |
| 6 | SRV_FORCE_NO_LOG_REDO | **不執行 Redo Log 前滾** (最暴力,直接掛載.ibd) | 數據可能停留在舊版本 |
核心限制:innodb_force_recovery > 0 時,實例進入 **唯讀模式** — 僅允許 SELECT、CREATE、DROP,**禁止 INSERT/UPDATE/DELETE**。
MySQL 5.5/5.6 源碼邏輯:啟動時等待 Purge Thread 啟動,但 force_recovery >= 2 會阻止 Master Thread,導致 Purge Thread 永不啟動,主線程陷入 Waiting for the background threads to start 無限循環。
解法:同時設置 innodb_purge_threads = 0 (禁用獨立 Purge Thread)。
my.cnf):
[mysqld]
innodb_force_recovery = 6
innodb_purge_threads = 0
systemctl restart mysqld
# 或 service mysql restart
日誌顯示 Crash recovery finished.、ready for connections. → **實例掛載成功!**mysqldump -u root -p --all-databases --single-transaction --routines --triggers > /backup/full_dump_$(date +%F).sql
必須加 --single-transaction** 保證導出一致性視圖 (雖然實例唯讀,但習慣加上)。# 註釋 force_recovery 恢復 purge_threads
# innodb_force_recovery = 6
# innodb_purge_threads = 0 (恢復預設 1 或 4)
systemctl restart mysqld # 正常模式啟動
mysql -u root -p < /backup/full_dump_$(date +%F).sql # 導入新實例
innodb_purge_threads 預設 1,force_recovery > 1 **必須** 設 innodb_purge_threads = 0。innodb_purge_threads 預設 1,同樣需關閉。innodb_purge_threads 可動態調整,但 force_recovery >= 2 仍建議暫設為 0 避免啟動卡死。force_recovery=6 跳過 Redo Log 前滾,**未提交事務視為已提交,已提交但未刷盤的事務可能丟失**。需結合 Binlog (若開啟) 或業務日誌對賬確認損失範圍。innodb_doublewrite=ON,預設開啟) — 防止頁寫入一半損壞。relay_log_info_repository=TABLE, master_info_repository=TABLE, relay_log_recovery=ON) — 從庫 Relay Log 元數據存表,避免 Error 1594/1236。innochecksum) 僅能校驗,無法修復邏輯錯誤。這次搶救雖然成功,但屬於「醫生開刀前先簽生死狀」的極端手段。**堅實的備份體系、健康的存儲硬體、Crash-Safe 配置,才是資料庫生存的根本**。