frm文件和ibd文件簡介
- 在MySQL中,使用默認的
存儲引擎innodb
創建一張表,那么在庫名
文件夾下面就會出現表名.frm
和表名.ibd
兩個文件ibd
文件是innodb的表數據
文件frm
文件是innodb的表結構
文件- 需要注意的是,
frm文件
和ibd文件
都是不能直接打開
的 - 恢復數據之前,需要先恢復表結構
- 需要注意的是,
- 在有建表語句的前提下,可以直接跳到
ibd文件恢復表數據
,不需要使用frm
文件恢復表結構
frm文件恢復表結構
- 前提是已經備份了對應的
frm
文件 - 建議重新啟動一個MySQL實例,待數據恢復后,通過
mysqldump
備份數據,再重新恢復到需要使用的數據庫里 - 在新啟動的實例上創建一個同名的表,例如
study.frm
,表示表名稱為study- 在不知道表結構的情況下,可以先定義一個字段,稍后可以通過
mysql.err
日志內查看表字段的數量
- 在不知道表結構的情況下,可以先定義一個字段,稍后可以通過
create table study (id int);
- 創建完表后,在對應的數據目錄下就會生成
study.frm
和study.ibd
文件,然后使用之前備份的study.frm
來替換現有的study.frm
,切記,不要着急替換study.ibd
文件,這個文件在恢復表結構后再使用- 注意替換文件后的
study.frm
文件的權限,確保和其他文件的屬主和屬組是一樣的 - 重啟mysql數據庫
- 注意替換文件后的
查看日志
grep study mysql.err | grep columns
容器啟動的MySQL,直接使用
docker restart <容器id>
來重啟MySQL服務如果是容器啟動的MySQL,可以使用下面的命令在容器外查看日志
docker logs <容器id> | grep study | grep columns
- 通過日志,我們可以看到,
study
這個表,之前有5個字段
,但是我們現在只有1個字段
[Warning] InnoDB: Table hello@002dworld/study contains 1 user defined columns in InnoDB, but 5 columns in MySQL. Please check INFORMATION_SCHEMA.INNODB_SYS_COLUMNS and http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue.
- 這個時候,我們可以把原來的表刪掉
drop table study;
- 然后重新創建一個和原來的表相同字段的表,切記,
表名稱要一樣
,字段內容不重要
,只需要字段數量一致
create table study (id1 int,id2 int,id3 int,id4 int,id5 int);
- 現在可以看到我們的建表語句了,當然,這個是上面使用的建表語句,咱們繼續往下
show create table study\G
*************************** 1. row ***************************
Table: study
Create Table: CREATE TABLE `study` (
`id1` int(11) DEFAULT NULL,
`id2` int(11) DEFAULT NULL,
`id3` int(11) DEFAULT NULL,
`id4` int(11) DEFAULT NULL,
`id5` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
- 確認是否開啟了
innodb_force_recovery
參數,正常情況下,如果不是為了恢復數據是不會開啟這個參數的innodb_force_recovery
參數需要配置到my.cnf
中的[mysqld]
模塊下,取值范圍是0-6
,默認是0
- 1: (SRV_FORCE_IGNORE_CORRUPT):
忽略檢查到的corrupt頁
- 2: (SRV_FORCE_NO_BACKGROUND):
阻止主線程的運行,如主線程需要執行full purge操作,會導致crash
- 3: (SRV_FORCE_NO_TRX_UNDO):
不執行事務回滾操作
- 4: (SRV_FORCE_NO_IBUF_MERGE):
不執行插入緩沖的合並操作
- 5: (SRV_FORCE_NO_UNDO_LOG_SCAN):
不查看重做日志,InnoDB存儲引擎會將未提交的事務視為已提交
- 6: (SRV_FORCE_NO_LOG_REDO):
不執行前滾的操作
- 當設置參數值大於0后,可以對表進行
select
、create
、drop
操作,但insert
、update
或者delete
這類操作是不允許的
grep 'innodb_force' my.cnf
- 插入配置到
my.cnf
配置文件中,然后再次替換study.frm
文件,並重啟MySQL服務- 注意替換文件后的
study.frm
文件的權限,確保和其他文件的屬主和屬組是一樣的
- 注意替換文件后的
sed -i '/\[mysqld\]/a\innodb_force_recovery=6' my.cnf
- 重啟完成后,再次查看建表語句
show create table study\G
*************************** 1. row ***************************
Table: study
Create Table: CREATE TABLE `study` (
`id` int(11) DEFAULT NULL,
`name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`time` int(11) DEFAULT NULL,
`lang` varchar(20) COLLATE utf8_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
- 到這里,我們已經成功找回之前的建表語句了,通過這個語句,就可以恢復之前的表了
- 復制獲取的建表語句,注釋掉之前的
innodb_force_recovery
參數,並且重啟MySQL服務
- 復制獲取的建表語句,注釋掉之前的
sed -i '/innodb_force_recovery/s/^\(.*\)$/#\1/g' my.cnf
- 再次刪掉
study
這個表
drop table study;
- 然后使用上面獲取到的建表語句重新建表,注意最后加上一個分號,這是SQL的語法格式
CREATE TABLE `study` (
`id` int(11) DEFAULT NULL,
`name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`time` int(11) DEFAULT NULL,
`lang` varchar(20) COLLATE utf8_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
ibd文件恢復表數據
-
在有建表語句的情況下,使用
idb
文件恢復數據,相比使用frm
文件恢復表數據要簡單方便很多 -
刪除當前的
ibd
文件
alter table study discard tablespace;
- 將之前備份的
study.ibd
文件復制到對應的數據目錄下,使用下面的命令將數據加載到MySQL數據庫里- 注意替換文件后的
study.ibd
文件的權限,確保和其他文件的屬主和屬組是一樣的
- 注意替換文件后的
alter table study import tablespace;
- 再次查看數據表,發現之前的數據也回來了
select * from study;
+------+------+------+----------+---------+
| id | name | age | time | lang |
+------+------+------+----------+---------+
| 1 | tom | 26 | 20211024 | chinese |
+------+------+------+----------+---------+
記得備份數據,數據是無價的
通過腳本利用ibd文件恢復數據
前提是表結構是存在的
注意自己的數據庫是否區分大小寫,以及表名稱是否有大小寫,如果表名稱有大小寫,新啟動的mysql一定要開啟大小寫[開啟大小寫參數:lower_case_table_names = 0]
mysql_user
變量的值為mysql數據目錄的屬主和屬組
根據實際場景修改mysql_cmd
變量的值,修改成自己用戶名
,用戶密碼
,主機ip
mysql_data_dir
變量的值為mysql數據存儲路徑
back_data_dir
變量的值為備份下來的ibd文件存儲路徑
#!/bin/bash
base_dir=$(cd `dirname $0`; pwd)
mysql_user='mysql'
mysql_cmd="mysql -N -uroot -proot -h192.168.70.49"
databases_list=($(${mysql_cmd} -e 'SHOW DATABASES;' | egrep -v 'information_schema|mysql|performance_schema|sys'))
mysql_data_dir='/var/lib/mysql'
back_data_dir='/tmp/back-data'
for (( i=0; i<${#databases_list[@]}; i++ ))
do
tables_list=($(${mysql_cmd} -e "SELECT table_name FROM information_schema.tables WHERE table_schema=\"${databases_list[i]}\";"))
database_name=${databases_list[i]/-/@002d}
for (( table=0; table<${#tables_list[@]}; table++ ))
do
${mysql_cmd} -e "alter table \`${databases_list[i]}\`.${tables_list[table]} discard tablespace;"
rm -f ${mysql_data_dir}/${database_name}/${tables_list[table]}.ibd
cp ${back_data_dir}/${database_name}/${tables_list[table]}.ibd ${mysql_data_dir}/${database_name}/
chown -R ${mysql_user}.${mysql_user} ${mysql_data_dir}/${database_name}/
${mysql_cmd} -e "alter table \`${databases_list[i]}\`.${tables_list[table]} import tablespace;"
sleep 5
done
done
通過shell腳本導出mysql所有庫的所有表的表結構
mysql_cmd
和dump_cmd
的變量值根據實際環境修改,修改成自己用戶名
,用戶密碼
,主機ip
databases_list
只排除了mysql的系統庫,如果需要排除其他庫,可以修改egrep -v
后面的值
導出的表結構以庫名來命名,並且加入了CREATE DATABASE IF NOT EXISTS
語句
#!/bin/bash
base_dir=$(cd `dirname $0`; pwd)
mysql_cmd="mysql -N -uroot -proot -h192.168.70.49"
dump_cmd="mysqldump -uroot -proot -h192.168.70.49"
databases_list=($(${mysql_cmd} -e 'SHOW DATABASES;' | egrep -v 'information_schema|mysql|performance_schema|sys'))
for (( i=0; i<${#databases_list[@]}; i++ ))
do
tables_list=($(${mysql_cmd} -e "SELECT table_name FROM information_schema.tables WHERE table_schema=\"${databases_list[i]}\";"))
[[ ! -f "${base_dir}/${databases_list[i]}.sql" ]] || rm -f ${base_dir}/${databases_list[i]}.sql
echo "CREATE DATABASE IF NOT EXISTS \`${databases_list[i]}\`;" >> ${base_dir}/${databases_list[i]}.sql
echo "USE \`${databases_list[i]}\`;" >> ${base_dir}/${databases_list[i]}.sql
for (( table=0; table<${#tables_list[@]}; table++ ))
do
${dump_cmd} -d ${databases_list[i]} ${tables_list[table]} >> ${base_dir}/${databases_list[i]}.sql
done
done