MySQL Innodb數據庫誤刪ibdata1後MySQL數據庫的恢復案例

 

上週,之前公司的同事朋友找我幫忙,看看可否幫忙恢復一個MySQL 數據庫,具體狀況爲:數據庫版本爲MySQL 5.6(具體版本不清楚),也不清楚具體的數據庫引擎; 沒有數據庫備份,只剩下數據庫下面的一些文件(frm、idb),具體緣由是由於出現問題的時候,重裝了MySQL,最要命的是ibdata1等文件也沒有了,固然這中間細節過程如何,不清楚也不用去糾結了。大概就是這麼一個狀況。 html

 

 

由於數據庫不大,將對應的文件拷貝到本身一臺測試服務器的MySQL數據文件目錄下後(下面實驗測試,對數據庫名等敏感信息作了一下混淆),以下所示,數據庫名爲test,show tables能夠看到相關的表。python

 

 

clip_image002

 

 

其中有幾張表的存儲引擎爲MyISAM,那麼這些表的數據是徹底能夠恢復的,可是大部分表的存儲引擎爲InnoDB,訪問表或查看錶都會提示ERROR 1146 (42S02): Table 'xxxx' doesn't exist 不存在。mysql

 

mysql> desc think_cache;
ERROR 1146 (42S02): Table 'test.think_cache' doesn't exist
mysql> show create table think_cache;
ERROR 1146 (42S02): Table 'test.think_cache' doesn't exist
mysql> 

 

因爲共享表空間的ibdata1數據文件不存在了,加之有沒有備份,因此我武斷的判斷這個數據庫真的沒法恢復了,可是事後一天,這個朋友跟我說找了一家數據恢復公司將這個數據庫恢復了。 聽到這個消息很有點學藝不精的尷尬(其實談不上尷尬吧,原本還在學習MySQL的路上,有些知識點不清楚也很正常。經驗是須要慢慢積累的),不過更多的是好奇別人是如何恢復數據的,既然別人可以恢復,那麼本身下一次遇到這種狀況也要能搞定。下面就來複盤一下別人是如何恢復數據的(其實只要稍稍作點功課,發現這個其實挺簡單的)git

 

首先,咱們來了解一下MySQL 表空間數據文件idbdat1文件相關概念和知識點:github

 

    InnoDB採用按表空間(tablespace)的方式進行存儲數據, 默認配置狀況下會有一個初始大小爲10MB, 名字爲ibdata1的文件, 該文件就是默認的表空間文件(tablespce file),用戶能夠經過參數innodb_data_file_path對其進行設置,能夠有多個數據文件,若是沒有設置innodb_file_per_table的話, 那些Innodb存儲類型的表的數據都放在這個共享表空間中,而系統變量innodb_file_per_table=1的話,那麼InnoDB存儲引擎類型的表就會產生一個獨立表空間,獨立表空間的命名規則爲:表名.idb. 這些單獨的表空間文件僅存儲該表的數據、索引和插入緩衝BITMAP等信息,其它信息仍是存放在共享表空間中。sql

 

    其實當時主要是對這個概念有點模糊了,覺得這個系統變量innodb_file_per_table默認是關閉的,數據都會存儲在共享表空間中,那麼這些文件刪除了,數據就沒法恢復。因此武斷的下結論,其實從MySQL 5.6.6開始, 系統變量innodb_file_per_table默認是啓用的。只要再多瞭解一點或者說更深刻了解一點的話,狀況就會立馬就會反轉。也就是說若是開啓了獨立表空間,可從ibd文件中恢復數據。即便共享表空間的數據文件idbdata1丟失也沒關係,反之,若是未開啓獨立表空間時,idbdat1被刪除了,數據也會被刪除,只能從備份中恢復,真的沒有其餘辦法。數據庫

 

 

那麼咱們接下來看看,如何從idb文件中恢復數據吧,咱們須要用到mysqlfrm工具, 須要安裝MySQL Utilities,下面是安裝MySQL Utilities 1.5.5服務器

 

# tar -xvf mysql-utilities-1.5.5.tar.gz session

# cd mysql-utilities-1.5.5app

# python ./setup.py build

# python ./setup.py install

 

 

提取frm文件的表結構信息

 

mysqlfrm 是一個恢復性質的工具,用來讀取.frm文件並從該文件中找到表定義數據生成CREATE語句。此處不對mysqlfrm工具作過多介紹,咱們使用msqlfrm來生成該數據庫的表的CREATE語句

 

[root@DB-Server ~]# service mysql stop
Shutting down MySQL.... SUCCESS! 
[root@DB-Server ~]# /usr/local/bin/mysqlfrm --basedir=/usr --port=3306 --user=root /data/mysql/test/ > test_frm.sql
[root@DB-Server ~]# 

 

檢查導出的SQL語句發現都是ERROR: The server version for this file is too low. It requires a server version 5.6.29 or higher but your server is version 5.6.20. Try using a newer server or use diagnostic mode這類錯誤

 

[root@DB-Server ~]# more test_frm.sql 
# Spawning server with --user=root.
# Starting the spawned server on port 3306 ... done.
# Reading .frm files
#
# Reading the think_cache.frm file.
ERROR: The server version for this file is too low. It requires a server version 5.6.29 or higher but your server is version 5.6.20. Try using a newer server or use diagnostic mode.
#
# Reading the think_session.frm file.
ERROR: The server version for this file is too low. It requires a server version 5.6.29 or higher but your server is version 5.6.20. Try using a newer server or use diagnostic mode.
#
# Reading the wx_activity_config.frm file.
ERROR: The server version for this file is too low. It requires a server version 5.6.29 or higher but your server is version 5.6.20. Try using a newer server or use diagnostic mode.
#
........................................................................................

 

從中能夠看到這個數據庫以前的版本爲MySQL5.6.29而我這裏的MySQL版本比這個低MySQL 5.6.20。因此必須找一個跟這個版本相同或高的MySQL數據庫操做才行。因而在另一臺測試服務器安裝了MySQL

 

[root@gettestlnx02 ~]# service mysqld stop
 
Stopping mysqld:  [  OK  ]
 
[root@gettestlnx02 tmp]# mv test  /data/mysqldata/mysql/test
 
[root@gettestlnx02 tmp]# cd /data/mysqldata/mysql/

 

/usr/bin/mysqlfrm --basedir=/usr --port=3306 --user=root /data/mysqldata/mysql/test/ > test_frm.sql

 

如何要查看輸出信息,可使用參數-vvv

 

/usr/bin/mysqlfrm --basedir=/usr --port=3306 --user=root -vvv /data/mysqldata/mysql/test/ > test_frm.sql

 

clip_image003

 

生成的SQL腳本沒有以分號結尾,原本想用sed命令給那些CREATE TABLE腳本加上分號結尾,可是發現其中大量CREATE TABLE的腳本結尾沒有規律,都是以COMMNET='xxxxx'結尾,只能手工添加分號(以下所示)

 

 

clip_image004

 

 

導入frm文件的表結構信息

 

mysql> use test;
Database changed
mysql> source test_frm.sql
Query OK, 0 rows affected (0.02 sec)
 
Query OK, 0 rows affected (0.01 sec)
 
Query OK, 0 rows affected (0.01 sec)
 
Query OK, 0 rows affected (0.00 sec)
.................................

 

而後咱們檢查這個數據庫的各種文件frm、ibd、MYI、MYD文件數量,後續作對比驗證用途。

 

[root@gettestlnx02 test]# ls -lrt *.frm | wc -l

84

[root@gettestlnx02 test]# ls -lrt *.ibd | wc -l  

84

[root@gettestlnx02 test]# ls -lrt *.MYI | wc -l     

22

[root@gettestlnx02 test]# ls -lrt *.MYD | wc -l 

22

[root@gettestlnx02 test]#

 

 

刪除新建表的獨立表空間文件

 

使用下面腳本生成刪除新建表的獨立空間的腳本:

 

select concat(concat('alter table ',table_name), ' discard tablespace;')                                                  
from information_schema.tables                               
where table_schema='test' and engine ='InnoDB';

 

使用腳本就能夠生成下面SQL,執行該命令後,對應數據庫下面的ibd文件所有被刪除。

 

alter table think_cache discard tablespace;      

alter table think_session discard tablespace;    

alter table wx_activity_config discard tablespace;

........................................

 

 

 

複製待恢復的表空間文件

 

將待恢復的ibd文件拷貝到對應數據庫目錄下面,並設置好權限屬性

 

# cd /tmp/database
# ls -lrt *.ibd | wc -l
84
# cp *.ibd /var/lib/mysql/test
 
# chown  mysql:mysql *.ibd
# chmod 660 *.ibd

 

 

導入表空間

mysql> alter table think_cache import tablespace;
Query OK, 0 rows affected, 1 warning (0.21 sec)
 
mysql> alter table think_session import tablespace;
Query OK, 0 rows affected, 1 warning (0.18 sec)
 
mysql> select count(*) from think_cache;
+----------+
| count(*) |
+----------+
|    10919 |
+----------+
1 row in set (0.01 sec)
 
mysql> select * from think_cache limit 5;
+---------------------------------------+------------+----------------------------------------+---------+
| cachekey                              | expire     | data                                   | datacrc |
+---------------------------------------+------------+----------------------------------------+---------+
| 00OLH9JvIwX42R3mPygXYN3gWZp2rH_rebate | 1533050257 | s:30:"00OLH9JvIwX42R3mPygXYN3gWZp2rH"; |         |
| 00SCWX7cIgqnnzHRArAXoascr1gnlA_rebate | 1516937278 | s:30:"00SCWX7cIgqnnzHRArAXoascr1gnlA"; |         |
| 00uVkAbOMPGQc2z02PPxVMblGY7oj7_rebate | 1528708564 | s:30:"00uVkAbOMPGQc2z02PPxVMblGY7oj7"; |         |
| 01dB7czgCph7hgm1qGM7qA7haChXop_rebate | 1525740805 | s:30:"01dB7czgCph7hgm1qGM7qA7haChXop"; |         |
| 023oMqQAAwg4WWxWgJSLNgQhYlgtVi_rebate | 1531560804 | s:30:"023oMqQAAwg4WWxWgJSLNgQhYlgtVi"; |         |
+---------------------------------------+------------+----------------------------------------+---------+
5 rows in set (0.00 sec)
 
mysql> select count(*) from think_session;
+----------+
| count(*) |
+----------+
|     1347 |
+----------+
1 row in set (0.00 sec)
 
mysql> select * from think_session limit 5;
+----------------------------+----------------+--------------+
| session_id                 | session_expire | session_data |
+----------------------------+----------------+--------------+
| 00onr4u3jabvi6vrts3bfeaqt4 |     1533358643 |              |
| 00rs65ljphuhhughujfnk2bci6 |     1533350110 |              |
| 01ld93n8ac31o4uorqrebtjir5 |     1533418040 |              |
| 01u5tv79pp8jjssh1r3s7oj6d4 |     1533351181 |              |
| 0261rcndf0jmq9dccou5l23mn4 |     1533346621 |              |
+----------------------------+----------------+--------------+
5 rows in set (0.00 sec)

 

 

 

導出數據庫

 

導入數據庫

 

 

若是順利的話,一切就正常了,數據正常恢復,是否也不是什麼難事,難就難在你不知道而已,若是你認證學習了一下這方面的知識點,整個事情其實並不複雜。有些細節操做問題能夠參考官方文檔:

 

https://dev.mysql.com/doc/refman/8.0/en/innodb-troubleshooting-datadict.html

https://dev.mysql.com/doc/refman/5.6/en/innodb-troubleshooting-datadict.html

 

 

 

那麼在這個恢復過程當中是否會遇到一些麻煩或問題呢,答案是確定的,下面簡單介紹一些在恢復過程當中可能遇到的問題

 

1: 在實驗測試過程,我一度使用版本爲MySQL 5.7.21的數據庫,在導入表空間是遇到下面錯誤:

 

mysql> alter table wx_sign_record import tablespace; 
 
ERROR 1808 (HY000): Schema mismatch (Table has ROW_TYPE_DYNAMIC row format, .ibd file has ROW_TYPE_COMPACT row format.)
 
mysql> alter table wx_sign_record  row_format=DYNAMIC;
 
ERROR 2013 (HY000): Lost connection to MySQL server during query
 
mysql> alter table wx_sign_record  row_format=DYNAMIC;
 
ERROR 2006 (HY000): MySQL server has gone away
 
No connection. Trying to reconnect...
 
Connection id:    2
 
Current database: test

 

後面在網上找到相關資料多是由於數據庫版本緣故我使用MySQL 5.6.41這個版本就沒有遇到這個問題。因此若是遇到這種數據恢復案例,最好使用相同的數據庫版本。

 

 

2:表空間 id 不一致問題

  

    我的沒有遇到這個問題,這裏不作介紹。能夠參考MySQL 數據恢復案例

 

 

3:腳本自動化問題 

 

   對於研究問題,能夠手工操做,可是最好經過腳本自動化操做,MySQL 數據恢復案例裏有自動化腳本放在github上,有興趣能夠參考!

 

 

 

原理介紹:

 

關於原理介紹,能夠參考英文原文The basics of InnoDB space file layout 或者MySQL 數據恢復案例下面這部份內容徹底摘抄自MySQL 數據恢復案例

 

恢復方案中,咱們使用到了 DISCARD TABLESPACE、IMPORT TABLESPACE 和修改表空間 id。咱們先說下 InnoDB 數據頁的組成。InnoDB 數據頁由 7 個部分組成,分別是 File Header、Page Header、Infimum 和 Supermum Records、User Records、Free Space 和 Page Directory。

接下來看看 ibdata 文件的組織結構,以下圖:

clip_image006
From
 blog.jcole.us, by Jeremy Cole.

而後看看 ibd 文件的組織結構,以下圖:

clip_image007
From
 blog.jcole.us, by Jeremy Cole.

咱們要修改的表空間 id位於 FSP_HEADER。不一樣的 ibd 文件表空間 id 是不一樣的。ibdata 文件中有一個數據字典 data dictionary,記錄的是實例中每一個表在 ibdata 中的一個邏輯位置,而在 ibd 文件中也存儲着一樣的一個 tablespace id,二者必須一致,InnoDB 引擎才能正常加載到數據。因此,咱們須要修改舊的表空間 id 爲新的。

實際上,咱們對於 ibdata 文件中的 undo、change buffer、double write buffer 數據能夠不用關心。咱們只須要利用一個全新的實例,以及一個乾淨的 ibdata 文件,經過卸載和加載表空間把 ibd 文件與 ibdata 文件關聯。筆者使用了這麼多腳本,目的就是如此。

 

 

參考資料:

 

https://serverfault.com/questions/698038/mysql-innodb-recovery-from-datafiles

https://dbarobin.com/2016/04/23/ibd-recovery/

相關文章
相關標籤/搜索