mysql数据库恢复数据_【技术分享】使用Innodb存储引擎的mysql数据库恢复
作者:維一零
預(yù)估稿費(fèi):400RMB(不服你也來投稿啊!)
投稿方式:發(fā)送郵件至linwei#360.cn,或登陸網(wǎng)頁版在線投稿
前言
某天,在測試一張新數(shù)據(jù)表的字段時,由于在phpmyadmin不斷地刪除該數(shù)據(jù)表,導(dǎo)致一時不慎將整個數(shù)據(jù)庫drop掉,當(dāng)時立馬就嚇尿了,那是我們運(yùn)營了一年的寶貴數(shù)據(jù),突然就全沒了,雖然還有兩個月前的一次備份數(shù)據(jù),但中間改動的很多新結(jié)構(gòu)以及產(chǎn)生的新數(shù)據(jù)都沒法恢復(fù)。情急之下,立刻關(guān)掉數(shù)據(jù)庫服務(wù)器,在網(wǎng)上尋找各種不靠譜的硬盤數(shù)據(jù)恢復(fù)軟件,試圖通過恢復(fù)mysql相關(guān)的數(shù)據(jù)庫文件來找回,但是都沒有效果,只能找到一些數(shù)據(jù)表的結(jié)構(gòu)文件(.frm),然而這些結(jié)構(gòu)我其實(shí)都有備份,沒什么卵用。由于之前網(wǎng)上看過通過mysql相關(guān)文件可以從不同機(jī)器恢復(fù)數(shù)據(jù),堅信恢復(fù)數(shù)據(jù)是可能的,所以不斷尋求方法。后來,先找了認(rèn)識的一個研發(fā)同事幫忙,他告訴我他都是用的別人管理的數(shù)據(jù)庫服務(wù),會自動備份,不需要自己維護(hù)數(shù)據(jù)庫,并且告訴我恢復(fù)不了;然后他又幫我找到了另一個DBA同事,來到了現(xiàn)場幫我看,看我沒有開啟mysql的binlog配置,又給了我一個結(jié)論:沒辦法恢復(fù)了!當(dāng)時我就真慌了,于是帶著壓力和微弱的信念,最終找到了可行的恢復(fù)方案,特此記錄。
這里事先說明一下,本人使用的mysql數(shù)據(jù)庫是集成在xampp套件下的默認(rèn)配置,并且我的數(shù)據(jù)庫表都是使用的Innodb存儲引擎(其他引擎有些好像還更好恢復(fù)),數(shù)據(jù)庫的表結(jié)構(gòu)也都事先有備份(沒備份也有辦法恢復(fù),可以從.frm文件或者后面提到的項目工具提供的一些功能)。由于mysql配置中"innodb_file_per_table=OFF",表示所有的數(shù)據(jù)庫表都在同一個數(shù)據(jù)空間,存儲在一個ibdata1文件中(在mysql數(shù)據(jù)根目錄中),所以最終我只需要將該ibdata1文件保存好(盡可能不被多次覆蓋,最好誤刪的第一時間就備份好),就可以使用下面的開源工具來提取數(shù)據(jù),恢復(fù)誤刪數(shù)據(jù)庫的所有數(shù)據(jù)。
準(zhǔn)備環(huán)境
獲取源碼
新建一個項目目錄undrop-for-innodb,進(jìn)入后使用git命令獲取源碼(或者解壓我附件打包的源碼):
git?clone?https://github.com/twindb/undrop-for-innodb.git
整個項目目錄結(jié)構(gòu)如下圖所示:
編譯項目
一條make命令直接編譯,成功后會生成3個程序:
導(dǎo)入測試數(shù)據(jù)
進(jìn)入測試數(shù)據(jù)庫目錄sakila,其中有該數(shù)據(jù)庫的各種數(shù)據(jù)表結(jié)構(gòu),先解壓數(shù)據(jù)備份文件:
tar?-zxvf?sakila-db.tar.gz
然后進(jìn)入mysql,新建一個數(shù)據(jù)庫sakila:
CREATE?DATABASE?IF?NOT?EXISTS?sakila?default?charset?utf8?COLLATE?utf8_general_ci;
這里我們先導(dǎo)入一張表actor的結(jié)構(gòu),然后導(dǎo)入全部數(shù)據(jù)(由于缺乏其它表結(jié)構(gòu)會報錯,忽略,確保actor的數(shù)據(jù)導(dǎo)入成功即可):
source?./actor.sql
source?./sakila-db/sakila-data.sql
確認(rèn)測試數(shù)據(jù)導(dǎo)入成功:
接著,高潮來了,將整個數(shù)據(jù)庫直接刪掉:
drop?database?sakila;
傻了吧,這時如果我告訴你恢復(fù)不了了,你是不是要偷笑了(幸虧不是你的數(shù)據(jù)庫)!別急,趕緊拷貝一份前面所說的數(shù)據(jù)空間文件ibdata1:
mkdir?backup
cd?backup/
sudo?cp?/var/lib/mysql/ibdata1?./
sudo?chmod?+r?ibdata1
OK,準(zhǔn)備工作已經(jīng)完成,下面就用編譯的undrop項目工具,來進(jìn)行數(shù)據(jù)恢復(fù)工作。
數(shù)據(jù)恢復(fù)
現(xiàn)在確認(rèn)一下數(shù)據(jù)恢復(fù)的必要條件:一份ibdata1數(shù)據(jù)文件,一份要恢復(fù)的數(shù)據(jù)庫的表結(jié)構(gòu)(如本文以測試數(shù)據(jù)庫sakila為例,恢復(fù)其中的actor表數(shù)據(jù),需要actor表的結(jié)構(gòu),具體在actor.sql描述如下圖;如果沒有此文件可用其他方法得到表結(jié)構(gòu),如下文將提到的.frm文件恢復(fù))。
解析數(shù)據(jù)文件
首先,由于mysql將Innodb驅(qū)動的數(shù)據(jù)使用B+tree索引在了數(shù)據(jù)空間文件ibdata1中,所以需要使用stream_parser工具進(jìn)行解析:
./../stream_parser?-f?./ibdata1
解析完成后,可以看到同目錄下生成一個pages-ibdata1目錄,其中包含兩個子目錄,一個是包含按索引排序的數(shù)據(jù)頁目錄,另一個是包含相關(guān)類型的數(shù)據(jù)目錄:
我們下面將主要關(guān)注的是第一個子目錄即索引好的數(shù)據(jù)頁目錄,因為我們要恢復(fù)的數(shù)據(jù)就在里面,其中第一個頁文件(0000000000000001.page)里包含所有數(shù)據(jù)庫的表信息和相關(guān)的表索引信息,類似一個數(shù)據(jù)字典,可以使用項目提供的一個腳本recover_dictionary.sh將其內(nèi)容放到一個test數(shù)據(jù)庫里詳細(xì)的查看,這里就不做演示了。
解析頁文件
既然第一個頁文件包含所有數(shù)據(jù)庫表的索引信息,我們就需要先解析它,以模擬mysql查詢數(shù)據(jù)的過程,最終才能找到要恢復(fù)的數(shù)據(jù)。c_parser工具可以用來解析頁文件,不過需要提供該頁文件的一個內(nèi)部結(jié)構(gòu)(表結(jié)構(gòu))。好在,undrop項目已經(jīng)幫我們準(zhǔn)備好了一切,項目根目錄下有個dictionary目錄,里面就包含數(shù)據(jù)字典用到相關(guān)表結(jié)構(gòu),如用來解析第一個頁文件的表結(jié)構(gòu)在SYS_TABLES.sql文件如下:
于是,就可以開始恢復(fù)工作了:
./../c_parser?-4Df?pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page?-t?./../dictionary/SYS_TABLES.sql??|?grep?actor
該命令使用c_parser工具解析數(shù)據(jù)庫表索引信息并過濾出我們想要恢復(fù)的actor表:
找到actor表后,得到該表的一個主索引值(如圖所示為25),通過這個索引值,再到另外一張表去查詢該actor表所有的索引信息,該表的結(jié)構(gòu)在"dictionary/SYS_INDEXES.sql"文件中可以看到,而此表對應(yīng)的數(shù)據(jù)頁文件是第三個數(shù)據(jù)頁0000000000000003.page,于是:
./../c_parser?-4Df?pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page?-t?./../dictionary/SYS_INDEXES.sql?|?grep?25
同樣能夠解析出相關(guān)的索引數(shù)據(jù):
這里到了關(guān)鍵的時候,上圖找到了actor表的兩個索引信息(消重后),分別是"PRIMARY"和"idx_actor_last_name",分別對應(yīng)于actor表結(jié)構(gòu)的主鍵和索引鍵idx_actor_last_name,其對應(yīng)在mysql存儲中的索引值為54和55,此索引值編號對應(yīng)的數(shù)據(jù)頁文件中即存儲了該索引的全部數(shù)據(jù)!所以解析的方法也差不多(有所差異,見如下命令),都需要輸入一個參數(shù)即該數(shù)據(jù)表的結(jié)構(gòu)以便能夠正確解析出數(shù)據(jù):
./../c_parser?-5f?pages-ibdata1/FIL_PAGE_INDEX/0000000000000054.page?-t?./../sakila/actor.sql?|?more
此處我們選擇的是主鍵索引對應(yīng)的數(shù)據(jù)頁文件進(jìn)行解析(另外一個索引鍵應(yīng)該也可以,只不過方法可能需要有所區(qū)別),終于順利解析見到了激動人心的數(shù)據(jù):
值得一提的是,如果所輸入的表結(jié)構(gòu)不正確(包括字段和索引,如果注釋太長可以去掉否則可能報錯),解析出來的數(shù)據(jù)就會出錯,顯示成錯位的狀況,這也是為什么我們需要事先拿到正確數(shù)據(jù)表結(jié)構(gòu)的原因。最后,只需要將其轉(zhuǎn)儲到一個sql文件里就可以方便導(dǎo)入到數(shù)據(jù)庫了:
./../c_parser?-5f?pages-ibdata1/FIL_PAGE_INDEX/0000000000000054.page?-t?./../sakila/actor.sql?>?./sakila_actor?2>?./sakila_actor.sql
此命令會在當(dāng)前目錄生成兩個文件(分別是sakila_actor和sakila_actor.sql),其中sakila_actor.sql只是一個引導(dǎo)性文件,其內(nèi)部調(diào)用命令語句“LOAD DATA LOCAL INFILE”加載sakila_actor文件內(nèi)的真實(shí)數(shù)據(jù),并且忽略外鍵檢查(“SET FOREIGN_KEY_CHECKS=0”)。所以,導(dǎo)入到數(shù)據(jù)庫之前,需要先根據(jù)數(shù)據(jù)表結(jié)構(gòu)建好相應(yīng)的數(shù)據(jù)表,再進(jìn)行加載:
CREATE?DATABASE?IF?NOT?EXISTS?sakila?default?charset?utf8?COLLATE?utf8_general_ci;
use?sakila;
source?./../sakila/actor.sql
source?./sakila_actor.sql????????#可能出錯,原因是sakila_actor.sql里sakila_actor的路徑寫成默認(rèn)的,需要調(diào)整成當(dāng)前路徑
至此,查詢一下該數(shù)據(jù)表,可以看到全部數(shù)據(jù)正常恢復(fù),使用mysqldump程序可以將其備份成可移植的sql數(shù)據(jù)文件,恢復(fù)工作順利完成!
恢復(fù)數(shù)據(jù)表結(jié)構(gòu)
前面提到數(shù)據(jù)表結(jié)構(gòu)的重要性,順便提一下數(shù)據(jù)表結(jié)構(gòu)的恢復(fù)。undrop項目提供sys_parser工具(默認(rèn)沒有編譯,需要自行安裝相關(guān)的開發(fā)包環(huán)境進(jìn)行編譯),據(jù)說可以從ibdata1文件恢復(fù)出表結(jié)構(gòu),本人沒有進(jìn)行試驗,只能說這是比較終極的數(shù)據(jù)恢復(fù)方案。這里主要提一下眾所周知的方法:從frm文件恢復(fù)數(shù)據(jù)表結(jié)構(gòu)。其實(shí)恢復(fù)過程比較簡單,就是需要拿到待恢復(fù)數(shù)據(jù)庫sakila的相關(guān)frm文件(默認(rèn)在“/var/lib/mysql/sakila”),如本例可以找到actor.frm文件,然后只要通過mysql新建一個測試數(shù)據(jù)庫,且在里面新建一張任意結(jié)構(gòu)的Innodb表actor,最后替換一下該測試數(shù)據(jù)庫下對應(yīng)的frm文件重啟mysql服務(wù)即可(其實(shí)在本人機(jī)器上測試,后面查詢表結(jié)構(gòu)時直接出錯,應(yīng)該和mysql的版本有關(guān)系,不過我相信眾位機(jī)油們這點(diǎn)小問題應(yīng)該不算什么哈)。
總結(jié)
本文就自己所遇到的場景做了一次數(shù)據(jù)恢復(fù)的演示,其本質(zhì)是利用Innodb引擎索引數(shù)據(jù)的原理來對數(shù)據(jù)空間文件的數(shù)據(jù)進(jìn)行提取。通過本文,希望遇到類似情形的朋友可以作為參考,不輕易放棄對數(shù)據(jù)的恢復(fù),同時也奉勸各位做好數(shù)據(jù)備份工作,因為偷懶少寫的那幾行代碼,有可能讓你付出慘痛的代價,天災(zāi)人禍無可避免。另外,服務(wù)器安全維護(hù)工作也很重要,如果被人拿走了本文所說的重要文件ibdata1(默認(rèn)需要root權(quán)限),那就和被人脫褲差不多了。
與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的mysql数据库恢复数据_【技术分享】使用Innodb存储引擎的mysql数据库恢复的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql查询不同老师所教不同课程_my
- 下一篇: ubuntu php 支持mysql_在