MySQL服務無法啟動的問題有可能是:
/A/ 第一反應
A/1 初始步驟
A/2 如果 MySQL崩潰
/B/ 識別問題
B/1 檢查日志
B/1.1 頁面損壞
B/1.2 InnoDB 時間旅行和日志序列號錯誤
B/1.3 數據字典錯誤
B/2 檢查表的錯誤
B/2.1 使用 CHECK TABLE / mysqlcheck
B/2.2 使用innochecksum
/C/ 恢復你的數據
C/1 MySQL Utilities / 從 .frm 文件中提取 CREATE TABLE 語句
C/1.1 下載和安裝 MySQL Utilities
C/1.2 從 .frm 文件中提取 CREATE TABLE 語句
C/2 損壞的表
C/2.1 使用 CREATE .. LIKE 恢復表
C/2.2 恢復多個/所有 InnoDB 數據庫并重新創建 ibdata/ib_log 文件
C/3 日志序列號不匹配/將來
C/3.1 刪除和重新創建數據
C/3.2 重新創建 ib_logfiles
C/3.3 執行引擎交換
C/4 數據字典問題
C/4.1 如何正確刪除 .ibd 文件
C/4.2 .ibd 文件丟失后重新創建表
C/4.3 .frm 文件存在但表不存在
C/4.4 孤立表或缺少 .frm 文件
以下是詳細的解決辦法:
/A/ 第一反應
A/1 初始步驟 - 停止、備份、重新啟動
1.停止MySQL 服務器。如果它已經離線或正在崩潰,請跳到第 2 步。
代碼:
/scripts/restartsrv_mysql--stop這里的目標是凍結數據和表文件的當前狀態,這樣就不會發生新的寫入,并且我們可以制作文件副本,而不用擔心會導致數據不一致或存儲信息丟失的更改。
2.備份您的數據和日志文件,如果不是整個 MySQL 數據目錄。
代碼:
mkdir /root/innodb.bak (orbackuppathofyour choice) cd /var/lib/mysql (oralternatedatadirectory,ifconfigured) ddif=ibdata1of=ibdata1.bakconv=noerror cp -p ./ibdata* /root/innodb.bak/ cp -p ./ib_log* /root/innodb.bak/首先,您要創建一個目錄來放置任何文件副本,然后,您要在 /var/lib/mysql(或您的數據目錄)中創建 ibdata1 文件的本地備份,以及 ibdata 的備份和 ib_logfiles 進入您的備份目錄。我喜歡同時使用 dd 和 cp 來制作 ibdata 文件的副本,因為這兩個實用程序之間的性質不同。dd 使用程序復制原始文件,而 cp 將文件的內容復制到新文件。我沒有經歷過任何特殊情況,這對恢復成功至關重要,但我認為這仍然是我的一個習慣,我懷疑這可能不是一個壞習慣。
理想情況下,特別是如果您還沒有備份,那么此時您可能會想要嘗試對您的數據目錄進行完整的復制。
代碼:
cp -Rp /var/lib/mysql{,.orig}我意識到這對于緊急情況下的某些人來說可能過于耗時或不切實際,所以如果這不可行,那沒關系 - 數據文件和 InnoDB 數據庫目錄至少應該提供一些可以在需要時依賴的東西。
3.備份你的 InnoDB 數據庫文件夾
假設您尚未備份完整的 MySQL 數據目錄,您仍然需要確保包含 InnoDB 表的任何數據庫也備份了各自的文件夾。如果您不確定您的哪些數據庫包含 InnoDB 表,您可以使用類似這樣的命令來檢查包含 .ibd 文件的目錄并將它們復制到您的備份文件夾(本示例中為 /root/innodb.bak - 另外,如果您的 DATADIR 不是默認值,則需要在開始時更新變量):
代碼:
DATADIR=/var/lib/mysql; find$DATADIR-typef -name *.ibd | awk -F/{print $(NF-1)}| sort | uniq | xargs -I {} cp -Rp$DATADIR/{} /root/innodb.bak4.啟動MySQL 服務器(如果可以的話)
此時,可以安全地使 MySQL 重新聯機,前提是您可以這樣做而不會導致崩潰。如果您可以將其聯機,請繼續并啟動 MySQL 服務,然后執行 mysqldump - 我建議執行以下操作(如果您愿意,可以將這些轉儲到 /root 以外的其他路徑 - 請記住您選擇的內容):
代碼:
/etc/init.d/mysql start mysqldump --single-transaction -AER >/root/dump_wtrans.sql mysqldump -AER >/root/dump.sql使用單事務標志轉儲它會在單個事務中創建轉儲,這可以防止鎖定數據庫,并且如果您正在運行 100% InnoDB 環境可能會有所幫助 - 所以為了安全起見,特別是如果您是不確定,我建議同時運行。
請務必檢查您的 SQL 轉儲內容以確保數據確實存在。在某些情況下,如果由于某種原因無法訪問數據,則只會出現表結構。如果您操作的數據庫經常運行 ALTER TABLE 命令,則在使用 --single-transaction 時尤其如此。如果 mysqldump 與特定表上的 ALTER TABLE 一致,則有可能只存在該結構。(在 MySQL 錯誤報告 71017中詳細討論)
注意:如果您正在處理文件系統損壞,請嘗試將這些文件備份到另一個磁盤驅動器(如果可用)(或者甚至備份到安全的遠程主機,如果可行)
A/2 如果 MySQL 崩潰
如果 MySQL 有崩潰,并拒絕重新啟動,那么這可能是您目前主要關心的問題。當然,您希望它在線用于生產目的,但最重要的是,讓 MySQL 在線可以讓您獲得數據的真實 MySQL 轉儲,這樣您就可以最大限度地減少永久丟失任何數據的機會,并幫助修復可能存在的表損壞。
因為 InnoDB 的 ACID 合規性(MySQL :: MySQL 5.6 Reference Manual :: 14.2.1 MySQL and the ACID Model),它遵守嚴格的數據一致性標準。這實質上意味著,如果它遇到任何數據問題,它幾乎總是會使 MySQL 崩潰,以防止進一步的一致性問題。從理論上講,這是一件好事,但在實踐中,計劃外停機絕不是一件好事。
然而,使用 innodb_force_recovery 選項通常可以幫助至少讓 MySQL 回到可訪問狀態。也就是說,最好知道它為什么有效,以及如何小心使用它。
使用 innodb_force_recovery
默認情況下,InnoDB 已經嘗試基本恢復步驟,當它發現問題時,但通常情況下,您需要添加innodb_force_recovery在您的 /etc/my.cnf 文件中設置以幫助它。這指示 InnoDB 以恢復模式啟動,告訴它跳過 InnoDB 啟動過程的各個部分,這通常是發生崩潰的地方。您需要從最低值 1 開始,然后僅根據需要增加它,最高可能值為 6。此設置在 my.cnf 文件的 [mysqld] 部分下輸入,如本示例所示:
代碼:
[mysqld]innodb_force_recovery=1您還可以運行以下單行命令將其自動添加到您的 /etc/my.cnf 文件中的正確部分下(將開頭的mode =變量中的數字更改為您想要使用的任何模式):
代碼:
mode=1; sed -i "/^\[mysqld\]/{N;s/$/\ninnodb_force_recovery=$mode/}" /etc/my.cnf然后,一旦您準備好將服務器恢復到默認模式,您可以使用以下命令刪除 innodb_force_recovery 行:
代碼:
sed-i/innodb_force_recovery/d/etc/my.cnf此配置選項不應用作保持服務器在線的長期甚至中期解決方案。如果您的服務器只能在啟用 innodb_force_recovery 的情況下保持在線,那么您的服務器上仍然存在需要解決的主要問題。如果 innodb_force_recovery 長時間處于活動狀態,您可能會在服務器上創建更多問題,特別是如果設置為高值(沒有充分的理由將 innodb_force_recovery 長時間設置為 6)。此模式完全是臨時的 - 僅用于恢復目的。
以下是每種模式的作用的簡要概述(每種模式也會自行組合,這意味著較高的值包含所有較低值的特征):
模式 1 - 當看到損壞的頁面時不會使 MySQL 崩潰模式 2 - 不運行后臺操作模式 3 - 不嘗試回滾事務模式 4 - 不計算統計數據或應用存儲/緩沖的更改模式 5 - 啟動期間不查看撤消日志模式 6 - 啟動期間不從重做日志 (ib_logfiles) 前滾例如:如果您的 MySQL 服務器以模式 3 啟動,而不是模式 2,則可以安全地假設崩潰與事務回滾過程有關。另外,請注意,從 MySQL 5.6.15 開始,模式 4-6 會將 MySQL 置于只讀模式。
如果您已經經歷了所有的 innodb_force_recovery 模式,并且您仍然因 InnoDB 錯誤而崩潰,那么下一個最佳舉措是嘗試收集一些有關可能導致崩潰的額外信息
。/B/ 識別問題
InnoDB 問題可以通過多種不同的方式彈出,雖然籠統的術語腐敗通常用于涵蓋很多問題——通常是不準確的——但嘗試具體確定你的問題通常是個好主意處理。
B/1 檢查日志
如果您懷疑 InnoDB 表或數據庫已損壞,可能是因為您注意到數據損壞、不存在的數據或拒絕啟動的 MySQL 服務。對于任何這些情況,您首先要查看的是 MySQL 錯誤日志。在典型設置中,這將位于 /var/lib/mysql/ 中,該文件將是您的主機名,帶有 .err 后綴。如果您不知道您的主機名,或者不想全部輸入(如果不是默認值,請將數據目錄替換為您自己的),這里有一個快速命令可以提取日志中的最后 200 行:
代碼:
tail-200/var/lib/mysql/`hostname`.err這將執行主機名命令,并使用返回的字符串代替主機名,這是命令行中反引號的功能。
您可能會在此處看到一些可以幫助您確定您遇到的腐敗類型(如果有)的內容。在本指南中,我將介紹您將遇到的三種最常見的與損壞相關的問題——頁面損壞、日志序列號問題和數據字典問題。以下是一些示例,并解釋了它們的含義:
B/1.1 頁面損壞代碼:InnoDB: Database page corruption on disk or a failedInnoDB: file read of page 515891.這通常會在之前提供更多信息,您應該注意這些信息,因為它可能包含一些有關此損壞具體發生在何處的有用信息,但最終這會告訴您 InnoDB 似乎認為您有頁面引用的頁面 ID 上存在損壞,或者可能只是無法以一種或另一種方式讀取文件。這不一定表明真正損壞的證據,事實上,在某些情況下,這可能只是操作系統損壞其自己的文件緩存的結果。因此,通常建議嘗試重新啟動計算機 - 當然是在進行備份之后 - 然后再繼續進行任何進一步的操作。也就是說 - 如果重新啟動確實解決了您的問題,您可能需要確保您的 RAM 沒有缺陷或即將淘汰,因為這可能是操作系統損壞其自己的文件緩存的常見原因之一。在嘗試任何恢復之前,您可能希望解決這種情況,以避免立即遇到同樣問題的風險。如果您不確定,或者您重新啟動后仍然懷疑存在損壞,您可以運行以下腳本來執行對所有 .ibd 文件進行innochecksum以嘗試識別損壞。如果 MySQL 仍然無法成功啟動,這將特別有用,因為它直接在文件上運行而無需 MySQL 訪問(事實上,如果它正在檢查的表空間在服務器上打開,它將無法工作):代碼:!/bin/bashfor i in $(ls /var/lib/mysql/*/*.ibd)doinnochecksum $idoneinnochecksum 實用程序查看表空間文件中的頁面,并計算每個頁面的校驗和。然后,它將這些中的每一個與存儲的校驗和進行比較,并讓您知道是否存在不匹配。如果是這樣,那通常表明頁面已以某種方式損壞。如果沒有發現不匹配,它將不會顯示任何輸出(除非包含 -v 用于詳細輸出)。如果 MySQL 在線且可訪問,您始終可以使用 CHECK TABLE 語句,如下所述:MySQL :: MySQL 5.5 Reference Manual :: 13.7.2.2 CHECK TABLE SyntaxB/1.2 InnoDB 時間旅行和日志序列號錯誤代碼:mysql: 120901 9:43:55 InnoDB: Error: page 70944 log sequence number 8 1483471899mysql: InnoDB: is in the future! Current system log sequence number 5 612394935.mysql: InnoDB: Your database may be corrupt or you may have copied the InnoDBmysql: InnoDB: tablespace but not the InnoDB log files. Seemysql: InnoDB: [url=http://dev.mysql.com/doc/refman/5.5/en/forcing-innodb-recovery.html]MySQL :: MySQL 5.5 Reference Manual :: 14.21.2 Starting InnoDB on a Corrupted Database[/url]首先,解釋一下究竟什么是日志序列號 (LSN)。對于 InnoDB 引擎上發生的每個操作,記錄都會寫入重做日志文件,默認情況下,通常在 MySQL 數據目錄中被視為 ib_logfile0 和 ib_logfile1。這兩個文件有一個固定的大小(在 MySQL 5.6.8+ 中,每個文件默認為 48M),記錄按順序寫入這些文件,從第一個日志文件開始直到它到達末尾,然后繼續第二個日志文件。一旦它到達第二個日志文件的末尾(假設只配置了 2 個默認日志文件 - 請參閱innodb_log_files_in_group),它會重新開始并再次開始在第一個日志文件的開頭寫入。這些記錄中的每一個都被賦予一個關聯的 LSN。此外,當數據庫被修改時,該數據庫中的特定頁面也會被賦予一個關聯的 LSN。在這兩者之間,這些 LSN 會一起檢查,以確保以正確的順序執行操作。LSN 本身本質上是日志文件的偏移量,存儲在數據庫頁面標題中的 LSN 告訴 InnoDB 需要刷新多少日志。在某個地方,無論是意外重啟、內存問題、文件系統損壞、復制問題、手動更改 InnoDB 文件還是其他原因,這些 LSN 都變得不同步。無論它是否使您的服務器崩潰,這都應該被視為合法的損壞,并且通常是您想要解決的問題B/1.3 數據字典錯誤代碼:[ERROR] Table ./database/table has no primary key in InnoDB data dictionary, but has one in MySQL!InnoDB: Error: table database/tableInnoDB: in InnoDB data dictionary has tablespace id 423,InnoDB: but tablespace with that id or name does not exist. HaveInnoDB: you deleted or moved .ibd files?[ERROR] Cannot find or open table database/table fromthe internal data dictionary of InnoDB though the .frm file for thetable exists. Maybe you have deleted and recreated InnoDB datafiles but have forgotten to delete the corresponding .frm filesof InnoDB tables, or you have moved .frm files to another database?or, the table contains indexes that this version of the enginedoesnt support.稍微解釋一下 InnoDB 的數據字典,它存在于系統表空間中——它本身作為存儲在 ibdata1 文件中的一組特殊頁面存在(系統表空間將始終被引用為空間 0)——并存儲任何表的元數據InnoDB 顯式處理的、列或索引。這不是結構元素的主要位置——它們位于每個 InnoDB 表的.frm文件中——但是,它確實包含許多相同的信息。這是您通常會看到導致這些錯誤的差異的地方。如果出于任何原因更改、移動、手動更改或替換了 ibdata1 文件 - 您會突然得到一個數據字典,它不能反映文件或數據庫結構中存在的內容。如果您已經閱讀了前面的錯誤描述,此時您應該明白,存在于 ibdata1(或以其他方式命名)文件中的數據與存在于各個表空間/.ibd/.frm 中的數據之間存在明顯的關聯文件。當這種關聯丟失或切斷時,可能會發生不好的事情。因此,您會看到出現此類數據字典錯誤的最常見原因是因為某些內容被移動或手動更改。它通常歸結為:數據字典希望此文件或表空間在此處,但它不是!,或.ibd/.frm 文件希望此項目在數據字典中,但它不是! 。再次記住,數據字典存儲在 ibdata 文件中,在大多數環境中,它只是存儲在 MySQL 數據目錄中的 ibdata1。B/2 檢查表的錯誤
日志通常是問題發生時的直接指示,但有時它們可能有點模糊。通常,您會看到損壞的跡象,但不知道哪些表、頁面或數據庫受到影響。檢查表的兩種與 InnoDB 相關的方法是CHECK TABLESQL 語句和innochecksum實用程序。您在此處使用的方法將取決于一個因素:您的 MySQL 服務器是在線還是離線。
MySQL 正在運行且可訪問?使用檢查表。innochecksum 不檢查服務器當前打開的表空間。MySQL 已崩潰或以其他方式脫機?innochecksum是這里的方法 - 它查看表空間文件中的頁面,計算每個頁面的校驗和,并將其與存儲的校驗和值進行比較。如果這些不匹配,MySQL 將崩潰,并且損壞或數據修改以某種方式顯而易見,因此這可能是確認表空間中的合法問題的可靠方法。B/2.1 使用 CHECK TABLE / mysqlcheck
CHECK TABLE 命令,也被mysqlcheck實用程序使用(特別是 -c 標志,但是 mysqlcheck 默認為這種行為),通過許多不同的確認和比較檢查來嘗試和識別腐敗的跡象。CHECK TABLE 和 mysqlcheck 都適用于 MyISAM和InnoDB 表,但是——在本文的上下文中——我將重點關注它對 InnoDB 表的作用。
請注意,mysqlcheck -r 的 REPAIR 功能和REPAIR TABLE MySQL 命令在 InnoDB 表上不起作用;mysqlcheck 主要僅在這種情況下用于識別問題 - 而不是解決問題。
以下是它在內部專門尋找的內容的細分:
存在相應的 .ibd 表空間文件。主索引一致性正確的順序(按鍵升序)唯一約束完好無損索引條目數對表中的所有其他索引重復步驟 1-5。最后,所有表都經過自適應哈希索引檢查。如果其中任何一個返回不正確或不一致的值,則該表可能被標記為已損壞。一旦表被標記為損壞,在問題得到解決或后續表檢查能夠確認問題不再存在之前,不能再使用該表。
在某些情況下,如果 CHECK TABLE 例程在 MySQL 自己遇到問題之前發現了 InnoDB 表的問題,這實際上可能會導致 MySQL 服務器被關閉以避免導致額外的錯誤。雖然這可能是一件好事,因為它可以幫助您阻止任何進一步的損害發生,但當您決定在 InnoDB 表上運行 CHECK TABLE 或 mysqlcheck 時,意識到這一點總是好的。
這不是發現的問題是簡單的損壞或錯誤的情況。發現的損壞/錯誤只會導致相應地標記索引/表。
運行 CHECK TABLE
CHECK TABLE 作為命令必須在 MySQL shell 中運行,或者通過 MySQL 在其他地方執行。例如,這是我通過將現有的 dictionary.ibd 文件替換為來自同一數據庫的另一個表的 .idb 文件而創建的一種情況,您可以在其中看到在普通表上執行的 CHECK TABLE 與已損壞或已損壞的表之間的比較遇到錯誤:
代碼:
mysql> CHECK TABLE roundcube.users; +-----------------+-------+----------+----------+| Table |Op| Msg_type |Msg_text| +-----------------+-------+----------+----------+ |roundcube.users| check |status| OK |+-----------------+-------+----------+----------+1rowinset (0.13sec) mysql> CHECK TABLE roundcube.dictionary; +----------------------+-------+----------+----------------------------------------------------------------+| Table |Op| Msg_type |Msg_text| +----------------------+-------+----------+----------------------------------------------------------------+ |roundcube.dictionary| check |Warning| InnoDB: Tablespace is missingfortable roundcube/dictionary || roundcube.dictionary |check| Error |Tableroundcube.dictionarydoesnt exist | | roundcube.dictionary | check | status | Operation failed | +----------------------+-------+----------+----------------------------------------------------------------+ 3 rows in set (0.00 sec)在這種情況下,遇到的錯誤實際上保證了服務器會自動關閉。我第一次在 roundcube.dictionary 上運行 CHECK TABLE 的那一刻,服務器崩潰了。這是因為我將活動的 MySQL 實例引入了問題的存在。
InnoDB 的數據一致性合規性堅持認為,一旦發現此類問題,就立即停止。根據確切觸發崩潰的原因,需要不同級別的 innodb_force_recovery 才能恢復 MySQL 服務器。在缺少表空間的情況下,最小值 - 1 - 可以正常工作。
請記住,MySQL 服務器因某種原因而被強制崩潰. 不要通過快速啟用 innodb_force_recovery 直接跳回 MySQL!這有可能,有時甚至是確定性,會導致比它解決的問題更多的問題。
運行 mysqlcheck
通常,您需要一次檢查多個表或數據庫。CHECK TABLE 不支持通配符,因此在檢查數據庫中的所有表或檢查服務器上的所有數據庫時使用起來很不方便。mysqlcheck - 默認情況下從命令行執行 CHECK TABLE - 通過允許您輕松檢查整個數據庫或所有數據庫來彌補這一點。在數據庫上執行 CHECK TABLE 的默認語法是(將 db_name 替換為您的數據庫名稱):
代碼:
mysqlcheckdb_name然后它將輸出對該數據庫中每個表執行 CHECK TABLE 的結果。如果您只想在數據庫中的幾個選擇表上執行它,您還可以在數據庫名稱之后指定表 (mysqlcheck db_name tbl1_name tbl2_name...)。
這是我在前一個場景中使用的圓形立方體數據庫上執行的示例:
代碼:
-bash-4.1mysqlcheck roundcuberoundcube.cache OK roundcube.cache_index OK roundcube.cache_messages OK roundcube.cache_thread OK roundcube.contactgroupmembers OK roundcube.contactgroups OK roundcube.contacts OK roundcube.cp_schema_version OK roundcube.dictionaryWarning:InnoDB:Tablespaceismissingfortableroundcube/dictionaryError:Tableroundcube.dictionarydoesnt exist status : Operation failed roundcube.identities Warning : InnoDB: Tablespace is missing for tableroundcube/identitiesError : Tableroundcube.identitiesdoesnt existstatus: Operationfailedroundcube.searches OK roundcube.session OK roundcube.system OK roundcube.users OK此外,您可以使用 -A 標志(或 --all-databases)對服務器上所有數據庫中的所有表執行 CHECK TABLE。
B/2.2 使用innochecksum
如前所述,InnoDB 需要有一致的數據,當它自己遇到校驗和不匹配時,它會立即停止一個活動的服務器。考慮到這一點,innochecksum 不僅可以幫助在事后識別損壞,而且可以幫助您總體上關注您的校驗和狀態。在預防的上下文中,這里涉及的唯一缺點是它不能在任何打開的表空間文件上運行。因此,為了獲得有關表校驗和狀態的任何體面圖片,需要使服務器脫機。
但是,因為我們正在處理的是崩潰的 MySQL 服務器上的損壞,所以這可能是您最不擔心的問題。innochecksum 在這些情況下非常適合跟蹤不匹配的校驗和,特別是因為它不需要服務器在線。
您從 innochecksum 獲得的輸出將根據發生的情況而有所不同,并且通常 - 除非您使用 -v 指定詳細輸出 - 除非實際發現問題,否則您將看不到任何輸出。這是在數據文件中發現的校驗和失敗的示例:
代碼:
page 8 invalid (fails old stylechecksum) page8:oldstyle: calculated =0x8195646B; recorded = 0x DA79A2EEinnochecksum 實用程序目前僅對特別引用的表空間文件 (.ibd) 進行操作,但您可以輕松地使用如下的 find 命令對所有 .ibd 文件執行 innochecksum(適當調整 DATADIR):
代碼:
DATADIR=/var/lib/mysql; find $DATADIR -type f -name *.ibd -o -name ibdata* | xargs -I{} innochecksum {}/C/ 恢復您的數據
一旦您確定了問題并適當地準備了您的服務器,您的下一步就是讓您的數據恢復正常工作。MySQL此時應該在線并且至少部分響應,無論是使用innodb_force_recovery還是其他方式。
C/1 MySQL 實用程序 / 從 .frm 文件中提取 CREATE TABLE 語句
MySQL 提供了一組可下載的實用程序,其中包括一些可能有助于恢復過程的工具——特別是,包括一個名為mysqlfrm的實用程序。此實用程序可以相當容易地從 .frm 文件中提取表的 CREATE TABLE 語句。這種說法可以極很有用,因為幾乎所有有用的恢復方法都涉及能夠重新創建您嘗試修復的原始表的結構,而且通常,這必須在沒有任何直接 MySQL 訪問原始表本身的情況下完成。
C/1.1 下載和安裝 MySQL 實用程序:
在此處下載軟件包。將其提取到您的服務器中的某處代碼:tar xvzf mysql-utilities*切換到解壓后的目錄,賦予 setup.py 執行權限,并運行它的構建和安裝操作代碼:cd mysql-utilities-1.4.3chmod +x setup.py./setup.py build./setup.py installC/1.2 從 .frm 文件中提取 CREATE TABLE 語句:
mysqlfrm將根據您現有的安裝創建自己的臨時 MySQL 守護程序,這意味著如果您現有的 MySQL 安裝已經在運行,您需要指定一個備用端口。這是我運行以從staff.frm文件中提取 CREATE TABLE 的示例:
代碼:
mysqlfrm --basedir=/usr --user=mysql --port=3308 /var/lib/mysql/testdb/staff.frm這是隨后的輸出:
代碼:
Spawning server with --user=mysql.Starting the spawned server on port 3308 ... done.Reading .frm filesReading the staff.frm file.CREATE statement for staff.frm:CREATETABLE`staff`(`staff_id`tinyint(3)unsignedNOTNULLAUTO_INCREMENT,`first_name`varchar(45)NOTNULL,`last_name`varchar(45)NOTNULL,`address_id`smallint(5)unsignedNOTNULL,`picture`blob,`email`varchar(50)DEFAULTNULL,`store_id`tinyint(3)unsignedNOTNULL,`active`tinyint(1)NOTNULLDEFAULT1,`username`varchar(16)NOTNULL,`password`varchar(40)CHARACTERSETutf8COLLATEutf8_binDEFAULTNULL,`last_update`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP, PRIMARYKEY(`staff_id`),KEY`idx_fk_store_id`(`store_id`),KEY`idx_fk_address_id`(`address_id`) )ENGINE=InnoDBDEFAULTCHARSET=utf8...done.從CREATE TABLE部分到CHARSET=utf8的上述輸出中的所有內容都是完整的、可執行的 CREATE TABLE 語句,它是重新創建具有正確結構的staff表所需的。要在 MySQL shell 中執行此操作,我只需粘貼該完整語句,并用分號 ( 結尾
。在某些情況下,您還很可能需要禁用外鍵檢查才能成功:
代碼:
SETFOREIGN_KEY_CHECKS=0;C/2 損壞的表
如果您確定服務器中當前存在損壞的表,您可以在此處采取一些方法,具體取決于嚴重程度。在幾乎所有表損壞的情況下,您至少需要在 innodb_force_recovery 設置為 1 的情況下運行 InnoDB,以允許 MySQL 在您使用它時保持在線。
C/2.1 使用 CREATE .. LIKE 恢復表
此方法的目標是嘗試并使用表的現有結構和數據,假設它們是可訪問的,以簡單地創建具有相同結構和數據的新表,以達到目的更換原件。基本步驟如下:
只需運行以下命令即可訪問 MySQL shell:代碼:mysql這引用了 /root/.my.cnf 文件以獲取憑據。如果它們是準確的,您將不需要從根 shell 提供憑據。否則,您需要手動提供:代碼:mysql -u root -p成功后,您應該會看到這樣的提示:代碼:mysql>運行以下 MySQL 語句,分別將 tablename 和 dbname 替換為您的表和數據庫的名稱:代碼:USE dbname;CREATE TABLE tablename_recovered LIKE tablename;INSERT INTO tablename_recovered SELECT * FROM tablename;如果您在這里沒有遇到任何問題,那么您可能很幸運。此時,您可以刪除原始表,并將_recovered表名稱更改回原始表:代碼:DROP dbname.tablename;RENAME TABLE dbname.tablename_recovered TO dbname.tablename;當涉及頁面損壞時,此方法是最簡單的,但可能成功率最低,因為它依賴于您能夠從功能上選擇所有數據并根據其結構創建恢復表。如果其中任何一個無法訪問或以其他方式無法讀取,則此方法可能會失敗。
但是,如果它在其中涉及增量插入的SELECT *部分失敗,您確實有另一個選擇。因此,您不必執行上面顯示的INSERT INTO ... SELECT * ...,而是執行以下操作:
代碼:
insertignoreintotablename_recoveredselect*fromtablenamelimit10;insertignoreintotablename_recoveredselect*fromtablenamelimit50;insertignoreintotablename_recoveredselect*fromtablenamelimit100;insertignoreintotablename_recoveredselect*fromtablenamelimit200; ...使用這種方法,您可以拼湊可訪問的數據,直到您到達故障點,此時您可能會失去與 MySQL 服務器的連接。
C/2.2 恢復多個/所有 InnoDB 數據庫并重新創建 ibdata/ib_log 文件
此方法的成功將再次取決于 mysqldump 從每個有問題的表中生成功能數據集的能力,但通常更全面的方法,因為它涉及初始化新的 ibdata 和 ib_log 文件。然而,正因為如此,如果不采取謹慎措施,這種方法也很有可能以失敗告終。確保您已經完成了 First Response 中的步驟,并且在繼續執行此操作之前,您有一組單獨的備份。
如果您已經有要在現有損壞的數據庫上恢復的備份轉儲,您也可以使用此方法 - 只需從第 2 步開始。
執行所有數據庫的 mysqldump。代碼:mysqldump -AER > /root/recovery_dump.sql如果您在此處遇到任何錯誤,請停下來仔細查看錯誤。如果它們表明您的任何重要數據已損壞到無法正確轉儲的程度,您可能不想繼續使用此方法。此外,請務必查看生成的轉儲文件,以確保它實際上包含預期的數據。刪除所有受影響的 InnoDB 數據庫。代碼:mysql> SET FOREIGN_KEY_CHECKS=0;mysql> DROP DATABASE db1;mysql> DROP DATABASE db2;...在禁用 innodb_fast_shutdown 后停止 mysqld,以確保執行干凈、完全的關閉。代碼:mysql -e "SET GLOBAL innodb_fast_shutdown = 0"/etc/init.d/mysql stop重定位 InnoDB 數據和重做日志文件代碼:mv /var/lib/mysql/ibdata* /tmp/mv /var/lib/mysql/ib_log* /tmp/注釋掉或刪除您當前在 /etc/my.cnf 中的任何 innodb_force_recovery 條目。代碼:sed -i /innodb_force_recovery/d /etc/my.cnf啟動 mysqld 并監控日志以確保它上線并適當地初始化數據和重做日志文件代碼:nohup /etc/init.d/mysql start & tail -f /var/lib/mysql/`hostname`.err恢復轉儲,一旦您確信 MySQL 仍然功能在線并準備好導入數據。代碼:mysql < /root/recovery_dump.sqlC/3 日志序列號不匹配/將來
作為一種嘗試確保您的數據保持一致并允許相當于撤消/重做功能的方式,InnoDB 在日志中維護所謂的日志序列號文件和表空間文件。每次對 InnoDB 表中的任何數據進行更改時,該更改都會導致日志序列號更新。這相當于一個偏移量,它指示 InnoDB 在文件中向前或向后看多遠,以便引用特定的數據狀態。
如果在任何時間點發生的事情導致一個更新,而不是另一個,你最終會在你的 MySQL 日志中看到關于不匹配的日志序列號的錯誤,或者日志序列號在未來。重要的是讓這些回到正軌,以便您的數據庫服務器再次正常運行
。C/3.1 刪除和重新創建數據
這是最有效也是唯一真實的解決方案,但不幸的是,對于許多人來說,它是最不適用的,因為在現實世界的情況下,并不是每個人都能輕松獲得這些數據(盡管如果你現在正在經歷這種體驗,它可能是考慮建立有效備份解決方案的好時機)。但是,如果您的 MySQL 實例沒有崩潰,并且您能夠創建一個 mysqldump,那么嘗試重新導入它當然值得一試。您可以按照恢復多個/所有 InnoDB中詳細說明的步驟進行操作Databases and Re-create ibdata/ib_log files" 方法在上述 Corrupted Tables 部分下嘗試恢復現有數據庫的轉儲
同樣,在此處進行任何更改之前,請確保您已經復制了所有重要的 ibdata、ib_logfile、.ibd 和 .frm 文件。
C/3.2 重新創建 ib_logfiles
如果您只處理單個 MySQL 實例,而不是 master->slave 或其他集群情況,這可能是一種有效的嘗試方法。這里的目標是將現有的 ib_logfiles 排除在外,允許 MySQL 在重新啟動時重新初始化它們。老實說,我用這種方法取得的成功相當有限,但它已經工作了足夠多的時間,值得一提:
代碼:
mysql -e"SET GLOBAL innodb_fast_shutdown = 0"/etc/init.d/mysql stop cd /var/lib/mysql mv ib_logfile0 ib_logfile0.bak mv ib_logfile1 ib_logfile1.bak /etc/init.d/mysql start第一個命令確保 InnoDB 執行徹底關閉,這有時可以幫助解決這種情況,值得在這里包括。
C/3.3 執行引擎交換
這是另一種相當激烈的方法,盡管它很嚴厲,但在我個人的經驗中似乎有相當可靠的成功率,盡管它肯定取決于你正在使用什么樣的環境. 此方法還要求 MySQL 能夠成功啟動。
要將數據庫中的所有表從 InnoDB 轉換為 MyISAM,請運行 MySQL 以下命令,將 db_name 替換為相關數據庫名稱:代碼:mysql -e "SELECT concat(ALTER TABLE , TABLE_NAME, ENGINE=MYISAM;) FROM Information_schema.TABLES WHERE TABLE_SCHEMA = db_name AND ENGINE = InnoDB AND TABLE_TYPE = BASE TABLE"然后,在停止 MySQL 之后,您希望將 ibdata* 和 ib_logfiles 排除在外:代碼:/etc/init.d/mysql stopmkdir -p /root/innodb.bakmv ib* /root/innodb.bak//etc/init.d/mysql start現在您已經使用 MyISAM 啟動了 MySQL 表,是時候將它們轉換回 InnoDB,手指交叉(再次將 db_name 替換為您的數據庫名稱):代碼:mysql -e "SELECT concat(ALTER TABLE , TABLE_NAME, ENGINE=InnoDB;) FROM Information_schema.TABLES WHERE TABLE_SCHEMA = db_name AND ENGINE = MyISAM"C/4 數據字典問題
雖然這些錯誤各不相同,但最常見的數據字典問題與表空間或表文件不處于 InnoDB 期望它們所處的狀態有關,通常是由于不正確刪除 InnoDB .ibd 或 . frm 文件,或由于刪除/移動 ibdata 文件。考慮到這一點,現在是注意以下事項的好時機:
C/4.1 如何正確刪除 .ibd 文件
數據庫各自子目錄中的 .ibd 文件表示這些數據庫中表的表空間。刪除文件本身會導致 InnoDB 為維護數據一致性而保留的記錄出現許多問題。如果你只需要出于任何原因刪除表空間(例如嘗試導入新的表空間/ibd 文件),正確的方法是使用 ALTER TABLE ... DISCARD TABLESPACE 語句,例如:
代碼:
mysql -e "ALTERTABLEroundcube.staff DISCARDTABLESPACE"在上面的例子中,roundcube 是數據庫,staff 是表。如果您在執行此操作后檢查數據庫目錄,您會注意到雖然該表的 .frm 文件仍然存在,但 .ibd 文件不存在。但請記住,*表*條目本身仍然存在于服務器上**。
注意:通常,您必須在執行此操作之前禁用 foreign_key_checks,這可以通過以下方式完成:
代碼:
SETFOREIGN_KEY_CHECKS=0;C/4.2 .ibd 文件丟失后重新創建表
如果您刪除或更改了表空間 (.ibd) 文件,您很可能會遇到如下所示的錯誤:
代碼:
[ERROR] MySQL is trying toopenatablehandle but the .ibd filefortabledbname/tblname doesnotexist.在這種情況下,它仍然認為該表以某種方式存在,這意味著在解決該問題之前您將無法正確地重新創建該表。幸運的是,盡管執行失敗,InnoDB 足夠聰明,可以意識到發生了什么,并且如果您運行以下命令(在適當的地方替換 dbname 和 tblname),它會執行幾個方便的過程:
第一步是嘗試擺脫表空間的剩余部分:代碼:ALTER TABLE dbname.tblname DISCARD TABLESPACE;它可能會或可能不會在 MySQL shell 上出錯,但如果您查看錯誤日志,它仍然會繼續并清理緩沖區:代碼:InnoDB: Warning: cannot delete tablespace 251 in DISCARD TABLESPACE.InnoDB: But let us remove the insert buffer entries for this tablespace.然后,嘗試刪除實際的表記錄(如果您需要保存 .frm 文件以再次獲取 create table 語句,請確保在執行此步驟之前已復制它):代碼:DROP TABLE dbname.tblname;您很可能會遇到類似的錯誤,但以下內容將再次出現在日志中:代碼:InnoDB: We removed now the InnoDB internal data dictionary entryInnoDB: of table `dbname/tblname`.之后,您可以使用備份或復制的 .frm 文件(使用上述 mysqlfrm 方法)重新創建表。C/4.3 .frm 文件存在但表不存在
這與上一期的思路相同,只是情況更簡單,更容易解決,最終:
代碼:
InnoDB: Cannotfindtabletest/child2 from the internal data dictionary InnoDB: of InnoDB though the .frm fileforthetableexists.在這里,最常見的問題是部分執行的 drop 或 alter 語句沒有導致所有表的文件都被正確刪除。在這些情況下,可以安全地刪除 .frm 文件,InnoDB 應該相應地進行調整。但是,我的建議不是刪除它,而是將其復制到備份文件夾中,至少是暫時的,以便您確實擁有 CREATE TABLE 語句,如果您出于任何原因需要訪問它。
如果您不打算刪除此表,或者 .ibd 文件已通過其他方式刪除,那么 - 除了通過挖掘 ibdata 文件嘗試進行一些深度數據恢復之外 - 您恢復此表的唯一選擇是從備份中恢復,因為 InnoDB 本質上是在錯誤中指出這個 .frm 文件絕對是有問題的表中唯一剩下的東西。如果是這種情況,絕對不要刪除 .frm 文件 - 只需重新定位它,這樣您就有機會重新創建表并重新構建數據。
C/4.4 孤立表或缺少 .frm 文件
如果 .frm 文件因任何原因丟失,您可能會看到如下錯誤:
代碼:
InnoDB:Error: table dbname/tblname already exists in InnoDB internalInnoDB: data dictionary. Have you deleted the .frm fileInnoDB: and not used DROP TABLE? ...在這種情況下,通常遵循此錯誤的說明描述了處理此錯誤的最有效方法:
代碼:
InnoDB: You candropthe orphanedtableinsideInnoDBbyInnoDB: creating anInnoDBtablewiththe samenameinanotherInnoDB:databaseandmoving the .frmfiletothecurrentdatabase.InnoDB:ThenMySQL thinks thetableexists,andDROPTABLEwillInnoDB: succeed.這意味著,如果您有孤立表的 CREATE TABLE 語句,無論是通過備份還是其他方式,您都可以簡單地創建一個測試數據庫,并在測試數據庫中創建表的副本(僅結構)。這將創建一個可用的 .frm 文件,該文件可用于復制到原始數據庫,并替換為替換丟失的 .frm,最后允許您刪除表。這是一個簡短的示例,假設原始數據庫和表名都只是測試:
代碼:
mysqlmysql>CREATE DATABASE test2;mysql>CREATE TABLE ... CHARSET=utf8;mysql>quitcp /var/lib/mysql/test2/test.frm /var/lib/mysql/test/mysqlmysql>SET FOREIGN_KEY_CHECKS=0;mysql>DROP TABLE test.test;結論
InnoDB 的數據一致性標準是一把雙刃劍。當它被仔細管理并充分了解它的運作方式時,它是一個了不起的引擎,但不幸的是,當它涉及到環境的變化時,它并不是最寬容的。它有一些自己處理情況的好方法,以及一些出色的錯誤日志記錄,但在確保環境穩定時,它絕對需要你注意。
我強烈建議,如果您有興趣閱讀 InnoDB 及其功能/特性,請查看以下一些閱讀材料:
MySQL :: MySQL 5.5 Reference Manual :: 14 The InnoDB Storage Engine On learning InnoDB :核心之旅——杰里米·科爾 您搜索了 Innodb - MySQL 性能博客
如果還是有問題,先不要驚慌,至少確保做了第一反應中描述的備份,這樣如果最后真的需要數據恢復服務,會有問題發生后不就的備份文件,以免因為問題發生后,因為寫入次數過多恢復不成功的情況。