利用 Forcing InnoDB Recovery 特性解決 MySQL 重啟失敗的問題


背景

小明同學在本機上安裝了 MySQL 5.7.17 配合項目進行開發,並且已經有了一部分重要數據。某天小明在開發的時候,需要出去一趟就直接把電腦關掉了,沒有讓 MySQL 正常關閉,重啟 MySQL 的時候,報錯如下:

...
[ERROR] InnoDB: Space id and page no stored in the page, read in are [page id: space=3611051955, page number=1571966525], should be [page id: space=86, page number=4]
...
[ERROR] InnoDB: Database page corruption on disk or a failed file read of page [page id: space=86, page number=4]. You may have to recover from a backup.
...
[ERROR] [FATAL] InnoDB: Aborting because of a corrupt database page in the system tablespace. Or,  there was a failure in tagging the tablespace  as corrupt.
...

分析

從日志內容來看,MySQL 在機器關機的時候有數據沒有落地,表空間損壞,導致重啟之后無法正常恢復,線程在數據頁中讀取不到需要的 page 和數據。

需要做特殊操作,讓 MySQL 跳過恢復,啟動 MySQL,然后把數據導出來,再重建數據庫導入。

MySQL 有個一個特性:Forcing InnoDB Recovery,啟用這個特性需要設置 innodb_force_recovery 大於 0。

innodb_force_recovery 可以設置為 1-6,大的值包含前面所有小於它的值的影響。

1 (SRV_FORCE_IGNORE_CORRUPT): 忽略檢查到的 corrupt 頁。盡管檢測到了損壞的 page 仍強制服務運行。一般設置為該值即可,然后 dump 出庫表進行重建。

2 (SRV_FORCE_NO_BACKGROUND): 阻止主線程的運行,如主線程需要執行 full purge 操作,會導致 crash。 阻止 master thread 和任何 purge thread 運行。若 crash 發生在 purge 環節則使用該值。

3 (SRV_FORCE_NO_TRX_UNDO): 不執行事務回滾操作。

4 (SRV_FORCE_NO_IBUF_MERGE): 不執行插入緩沖的合並操作。如果可能導致崩潰則不要做這些操作。不要進行統計操作。該值可能永久損壞數據文件。若使用了該值,則將來要刪除和重建輔助索引。

5 (SRV_FORCE_NO_UNDO_LOG_SCAN): 不查看重做日志,InnoDB 存儲引擎會將未提交的事務視為已提交。此時 InnoDB 甚至把未完成的事務按照提交處理。該值可能永久性的損壞數據文件。

6 (SRV_FORCE_NO_LOG_REDO): 不執行前滾的操作。恢復時不做 redo log roll-forward。使數據庫頁處於廢止狀態,繼而可能引起 B 樹或者其他數據庫結構更多的損壞。

注意:

為了安全,當設置參數值大於 0 后,可以對表進行 select, create, drop 操作,但 insert, update 或者 delete 這類操作是不允許的。MySQL 5.6.15 以后,當 innodb_force_recovery 的值大於等於 4 的時候,InnoDB 表處於只讀模式。

在值小於等於 3 時可以通過 select 來 dump 表,可以 drop 或者 create 表。MySQL 5.6.27 后大於 3 的值也支持 DROP TABLE;

如果事先知道哪個表導致了崩潰則可 drop 掉這個表。如果碰到了由失敗的大規模導入或大量 ALTER TABLE 操作引起的 runaway rollback,則可 kill 掉 mysqld 線程然后設置 innodb_force_recovery = 3 使數據庫重啟后不進行 rollback。然后刪除導致 runaway rollback 的表;

如果表內的數據損壞導致不能 dump 整個表內容。那么附帶 order by primary_key desc 從句的查詢或許能夠 dump 出損壞部分之后的部分數據;

若使用更高的 innodb_force_recovery 值,那么一些損壞的數據結構可能引起復雜的查詢無法運行。此時可能只能運行最基本的 select * from table 語句。

解決

前面說了,表空間損壞,重啟時前滾恢復失敗,因此在重啟的時候不要執行前滾的操作,在 /etc/mysql/my.cnf 中添加:

[mysqld]
innodb_force_recovery = 6

然后重啟 MySQL,立即對數據庫用 mysqldump 把數據導出。完成后,去掉 innodb_force_recovery 或者設置為 0,然后重新創建數據庫,把數據導入。

最后

這個方法僅僅是緊急情況下的一種補救,不能依賴於這個辦法,最好是做好數據備份工作,包括全備份和日志備份。確定要使用該方案是要確保有原始損壞數據的副本。4 以上的值可能永久導致數據文件損壞。務必在測試環境測試通過后再在生產環境使用。

MySQL crash 或者 MySQL 服務器 crash 會導致各種各種的問題 ,對於主從復制關系,MySQL 5.6 版本開始新增了 crash-safe 的特性,可以在最大程度上避免 error 1594 的問題,保證數據的安全,如何開啟這個功能可以參考上一篇博文:MySQL 5.6 從庫開啟 crash-safe 功能

作為一個 DBA,遇到問題,要淡定,細心閱讀日志,從中找的相關錯誤提示,然后依據錯誤找到相關的解決方法來解決問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM