MySQL 數據庫崩潰?InnoDB 恢復完整教學

MySQL 數據庫崩潰?InnoDB 恢復完整教學
前個禮拜,銅鑼灣有間網店打嚟,話個網站開唔到,後台入唔到,客落唔到單。IT 外判睇過,話 MySQL 起唔到,error log 寫住「InnoDB: Database page corruption on disk」。個 database 有成 40GB,係成間網店嘅命脈——會員資料、訂單、庫存,全部喺入面。老闆娘把聲都震:「今日係雙十一預熱,冇咗個網店真係會執笠。」今日講下 MySQL InnoDB 崩潰嘅原因、點樣用 innodb_force_recovery 救,同埋 .frm/.ibd 檔案點樣撈返。
InnoDB 係咩?點解會崩潰?
MySQL 有幾種儲存引擎,InnoDB 係預設嗰隻(MyISAM 係舊嘅,已經冇乜人用)。InnoDB 嘅特點係有 transaction、crash recovery 同埋 foreign key。但係佢都有弱點:
- 突然斷電:最常見。InnoDB 靠 redo log(ib_logfile)做 crash recovery,正常斷電重啟,佢會自動 replay 個 log。但係如果斷電嗰陣寫緊個 data page,而個 page 寫咗一半(torn page),下次開機就會報 page corruption。
- 硬碟壞道:ibdata1 或者 .ibd 檔橫跨好多 sector,壞道啱啱喺個 page 上面就會損壞。MySQL 會報「Database page corruption」或者「Checksum mismatch」。
- MySQL 升級失敗:大版本升級(例如 5.7 上 8.0),如果中途出事,個 data dictionary 可能會唔一致。8.0 嘅 data dictionary 架構同 5.7 好唔同,搞彎咗好麻煩。
- 磁碟滿咗:個 disk 寫滿嗰陣,InnoDB 寫唔到 redo log 或者 data page,會直接 crash。之後清返位都開唔返,因為已經有啲 page 寫咗一半。
- 人為誤刪:有人 rm -rf 錯 folder,或者 DROP DATABASE 撳錯。呢種唔係 corruption,係邏輯刪除,要用唔同方法救。
第一步:睇 Error Log,唔好亂試
MySQL 崩潰嗰陣,第一時間唔係 reboot,而係睇 error log(通常喺 /var/log/mysql/error.log 或者 datadir 入面嘅 .err 檔)。常見錯誤:
- "InnoDB: Database page corruption on disk":有 data page 損壞,要記低邊個 page number(例如 page 12345),之後有用。
- "InnoDB: Failed to read page":讀唔到,可能係硬碟問題,先檢查 S.M.A.R.T.。
- "[ERROR] InnoDB: Log scan aborted":redo log 損壞,crash recovery 做唔到。呢種要用 innodb_force_recovery 跳過。
- "Table 'xxx' doesn't exist in engine":個 .frm(表結構)同 .ibd(資料)唔 match,多數係升級或者搬遷出事。
1. 即刻備份成個 datadir(/var/lib/mysql),原封不動抄多一份
2. 唔好 delete 任何 ib_logfile 或者 ibdata1——佢哋入面可能有未寫入嘅 transaction
3. 唔好喺原機上面亂試 innodb_force_recovery——應該喺備份上面試
innodb_force_recovery:由 1 到 6 逐級試
呢個係 MySQL 內建嘅救命參數,加喺 my.cnf 嘅 [mysqld] 下面:
innodb_force_recovery = 1
由 1 開始試,逐級加上去,唔好一嚟就 set 6。每個級別做咩:
- Level 1:跳過 crash recovery 嘅部份檢查。最溫和,如果係輕微損壞,呢個已經開得返。
- Level 2:唔做 background purge,淨係讀。如果 purge 線程嗰陣 crash,用呢個。
- Level 3:唔做 transaction rollback。未 commit 嘅 transaction 會留喺度,但係至少開得返。
- Level 4:唔做 insert buffer merge。如果 insert buffer 損壞,用呢個。
- Level 5:唔做 undo log rollback,淨係讀。呢個時候個 database 係唯讀,唔寫得。
- Level 6:終極模式,唔做 redo log,直接讀 data page。連 crash recovery 都 skip,開得返嘅機會最大,但係資料可能唔一致。
正確用法:set 咗之後 restart MySQL,如果開得返,即刻用 mysqldump 將啲資料倒出嚟:
mysqldump -u root -p --all-databases --single-transaction > /backup/full_dump.sql
倒完之後,起個全新嘅 MySQL,將個 dump 還原入去。唔好繼續用個損壞咗嘅 datadir——佢已經唔可信。
注意:innodb_force_recovery > 0 嗰陣,MySQL 係唔俾寫入(INSERT/UPDATE/DELETE 會報錯)。呢個係保護機制,唔好諗住長期用呢個模式跑住先。
銅鑼灣個案:Level 4 救返個網店
講返開頭嗰單。40GB database,error log 話有幾個 page corruption。我哋嘅處理:
- 完整備份 datadir:40GB,抄咗約 25 分鐘。之後所有操作喺備份上面做。
- 逐級試 force_recovery:Level 1、2、3 都開唔到,停喺同一個位。Level 4 成功開到——證明係 insert buffer 損壞。
- mysqldump 倒出:用 --single-transaction + --skip-lock-tables,逐個 database 倒。有兩個 table 倒嗰陣報錯,話某啲 row 讀唔到,skip 咗佢哋先,之後再逐行試。
- 起新機還原:喺我哋部測試機起個全新 MySQL 8.0,將個 dump 還原。還原完做 CHECK TABLE,確認冇問題。
- 補返唔見嘅資料:有兩張訂單表唔見咗約 200 行(最近兩日嘅單)。客嗰邊有 payment gateway 嘅記錄,我哋幫佢哋寫咗個 script,由 Stripe/PayPal 嘅 API 撈返啲訂單資料,補返入去。
成單用咗一日半,收 $8,500。老闆娘即刻叫我哋幫佢哋 set 自動 backup——每日 mysqldump + 每小時 binlog backup,異地存放。而家佢哋瞓得安樂好多。
.frm 同 .ibd 檔案點樣撈?
有時個 ibdata1(系統表空間)死咗,但係每個 table 嘅 .ibd 檔仲喺度。呢種可以用「discard/import tablespace」嘅方法救:
- 喺新嘅 MySQL 起返同名同結構嘅空 table(要搵返個 CREATE TABLE 語句,如果冇,可以用 mysqlfrm 或者 ibd2sdi 工具由個 .ibd 反推出嚟)
- 喺新 table 上面跑:
ALTER TABLE your_table DISCARD TABLESPACE;
- 將舊嘅 .ibd 檔抄去新 database 嘅目錄,覆蓋個空嘅 .ibd
- 再跑:
ALTER TABLE your_table IMPORT TABLESPACE;
- MySQL 會檢查個 .ibd 嘅 page 同 table 結構係咪 match,match 就掛載成功
呢招喺 MySQL 8.0 複雜啲,因為 data dictionary 唔再係 .frm 檔,而係喺 mysql.ibd 入面。如果連 mysql.ibd 都死,就要用 ibd2sdi 逐個 .ibd 抽個 SDI(serialized dictionary information)出嚟,人手重建個表結構。我哋有工具同經驗做呢啲,但係 DIY 好易搞錯。
Binlog:最後嘅救命稻草
如果 mysqldump 倒唔返,或者唔見咗最近嘅資料,可以睇下有冇開 binlog(binary log)。Binlog 記低咗所有寫入操作,可以用嚟重播:
# 睇下有邊啲 binlog SHOW BINARY LOGS; # 將某段時間嘅 binlog 導出做 SQL mysqlbinlog --start-datetime="2026-10-01 00:00:00" --stop-datetime="2026-10-06 12:00:00" /var/log/mysql/mysql-bin.000123 > /backup/binlog_recover.sql
但係有個前提:binlog 要有開,同埋未過期(expire_logs_days)。好多細公司根本冇開 binlog,出事嗰陣先發現冇。我哋幫客 set MySQL,第一件事就係開 binlog,保留 7 日。
點樣預防 MySQL 崩潰?
- 開 binlog:上面講咗,救命用。my.cnf 加:
log_bin = /var/log/mysql/mysql-bin.log expire_logs_days = 7 server-id = 1
- 自動備份:每日 mysqldump(full),加 binlog(incremental)。寫個 cronjob,唔好靠人手。我哋有現成 script 可以俾客。
- 監察磁碟空間:MySQL 死嘅常見原因係 disk full。Set 個 alert,低過 20% 就通知。唔好等到寫唔到先知。
- 用好嘅硬碟:Database 唔好放喺普通 desktop 碟。用企業級 SSD 或者 NAS 碟,開返 RAID。我哋見過太多 database 放喺單隻平價碟,死嗰陣喊都無謂。
- 定期做 CHECK TABLE:每個月跑一次,及早發現損壞:
mysqlcheck -u root -p --all-databases --check
- 升級之前備份:大版本升級(5.7→8.0)之前,一定要做完整備份。升級失敗 rollback 唔到,有 backup 先至唔驚。
收費參考
- innodb_force_recovery 救到,mysqldump 倒出:$4,000-$7,000,1 日
- 要逐表處理、補資料:$7,000-$12,000,2-3 日
- .ibd 逐個撈、重建表結構:$10,000-$20,000,3-5 日,視乎 table 數量
- 硬碟物理損壞:另加硬碟恢復費用,$6,000 起
FAQ
1. 問:MySQL 開唔到,我可唔可以 delete 咗 ib_logfile0 同 ib_logfile1 等佢重建?
答:千祈唔好!呢兩個檔入面可能有未寫入 data file 嘅 transaction,delete 咗就真係唔見。正確做法係先備份成個 datadir,然後喺備份上面試 innodb_force_recovery。如果真係要重建 log,都要喺確定冇重要 uncommitted transaction 之後先做。
2. 問:MyISAM 同 InnoDB 邊個易救啲?
答:MyISAM 結構簡單啲(.frm + .MYD + .MYI),用 myisamchk 工具修,唔識嘅人都做到。但係 MyISAM 冇 transaction,crash 嗰陣易爛。InnoDB 複雜啲,但係有 crash recovery 機制,輕微損壞會自己修好。總體嚟講,而家應該全部用 InnoDB,唔好再用 MyISAM。
3. 問:個 database 有 200GB,mysqldump 會唔會好慢?
答:會。200GB dump 出嚟可能要幾個鐘,還原仲耐。有幾個加速方法:用 --single-transaction 唔鎖表、dump 嗰陣唔好做其他嘢、還原嗰陣可以調大 innodb_buffer_pool_size。如果真係太大,可以考慮用 Percona XtraBackup 做物理備份,快好多。我哋有大 database 處理經驗,可以幫手設計備份策略。
4. 問:雲端 RDS(例如 AWS RDS、阿里雲)係咪唔使擔心?
答:冇咁樂觀。RDS 係幫你處理咗硬件同埋基本備份,但係邏輯損壞(例如誤 delete、程式 bug 寫錯資料)佢救唔到你。而且 RDS 嘅自動備份保留期有限(通常 7-35 日),過咗就冇。重要資料都係要自己做多份異地備份,唔好完全靠雲。
總結
MySQL 崩潰唔使慌,記住:備份 datadir → 睇 error log → 逐級試 innodb_force_recovery → mysqldump 倒出 → 起新機還原。唔好 delete log 檔,唔好喺原機亂試,唔好諗住長期用 force_recovery 模式跑。如果搞唔掂,WhatsApp +852 6558 6806,我哋處理過幾十 GB 大嘅 MySQL 恢復,免費初步評估。
想學多啲?睇下 數據備份服務,或者 3-2-1 備份法則,未雨綢繆好過臨急抱佛腳。
覺得這篇文章有幫助?
歡迎分享給您的朋友或同事。