使用Shell恢复MySQL .frm和.ibd文件

这是学习笔记的第 1901篇文章


MySQL里面对于表的默认的配置是每个表都有独立的文件.ibd和.frm文件对应,对于数据恢复来说,会提供很大的便利。 

其中.frm文件存储了表结构定义信息,而.ibd文件存储了真正的数据。如果某种特殊情况下,你只有.frm文件和.ibd文件,能不能单独恢复出来数据呢,答案是肯定的,当然这个过程不是一个命令搞定,而是需要一些方法和技巧。

比如.frm文件,我们拿到这个二进制文件的时候,其实我们也不知道里面到底有多少字段,怎么把DDL结构解析出来呢,这是第一个问题,而这个问题解决了之后,后续的问题其实就迎刃而解,我们可以完全使用迁移表空间的方式来处理。

所以在恢复.frm和.ibd文件的时候,难点在于如何解析得到建表的DDL语句。

在这里我们要做个小把戏,需要预创建一个同名的表,然后通过交换frm文件来变相得到DDL语句。这个脚本为了支撑后续的灵活性,我是单独创建了一个数据库test_recover,可以作为你的一个专用恢复数据库,可以在上面做大量的恢复测试,来充分验证方案的可行性。

所以对于test_recover我们可以默认设置一些基本的配置。

解析得到DDL的脚本get_tab_ddl.sh如下:

SOURCE_DUMP_PATH='/tmp'
RECOVER_TABLE_NAME='userscore_255'
MYSQL_DATA_DIR=/data/mysql_5727/data
MYSQL_LOG_DIR=/data/mysql_5727/log/mysql.err
MYSQL_CONN_STR="/usr/local/mysql/bin/mysql --socket=/data/mysql_5727/tmp/mysql.sock --port=5727 -Ne"
MYSQL_RECOVER_DB=test_recover
MYSQL_CON_FILE=/data/mysql_5727/my.cnf
SHUTDOWN_MYSQL="mysqladmin shutdown --socket=/data/mysql_5727/tmp/mysql.sock --port=5727"
STARTUP_MYSQL="/bin/sh /usr/local/Percona-Server-5.7.16-10-Linux.x86_64.ssl101/bin/mysqld_safe --defaults-file=/data/mysql_5727/my.cnf "

ls -l ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}

${MYSQL_CONN_STR} "select now()"
${MYSQL_CONN_STR} "create database if not exists ${MYSQL_RECOVER_DB} ; use ${MYSQL_RECOVER_DB};create table if not exists ${RECOVER_TABLE_NAME}(id int);"

ls -l ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}

rm -f ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.ibd
#mv ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm.bak
#mv ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.ibd ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.ibd.bak

/bin/cp ${SOURCE_DUMP_PATH}/${RECOVER_TABLE_NAME}.frm ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm

chown -R mysql:mysql ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm

${MYSQL_CONN_STR} " flush tables;use ${MYSQL_RECOVER_DB};show create table ${RECOVER_TABLE_NAME}\G"

COLUMN_NUM=`tail -5000 $MYSQL_LOG_DIR |grep "contains 1 user defined columns in InnoDB, but" |tail -1|awk -F, '{print $2}'|awk '{print $2}'`
echo 'Generate dynamic columns...'$COLUMN_NUM
echo "create table ${MYSQL_RECOVER_DB}.${RECOVER_TABLE_NAME} (" > $SOURCE_DUMP_PATH/ddl_${RECOVER_TABLE_NAME}.sql
for ((i=1; i < COLUMN_NUM; i++))
do
echo "id${i} int," >> $SOURCE_DUMP_PATH/ddl_${RECOVER_TABLE_NAME}.sql
done
echo "id$COLUMN_NUM int);" >> $SOURCE_DUMP_PATH/ddl_${RECOVER_TABLE_NAME}.sql

${MYSQL_CONN_STR} "use ${MYSQL_RECOVER_DB};drop table ${RECOVER_TABLE_NAME};"
${MYSQL_CONN_STR} "source $SOURCE_DUMP_PATH/ddl_${RECOVER_TABLE_NAME}.sql"

#create table ${RECOVER_TABLE_NAME}(id1 int,id2 int,id3 int,id4 int,id5 int,id6 int,id7 int)"


/bin/cp -f ${SOURCE_DUMP_PATH}/${RECOVER_TABLE_NAME}.frm ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm
chown -R mysql:mysql ${MYSQL_DATA_DIR}/${MYSQL_RECOVER_DB}/${RECOVER_TABLE_NAME}.frm

sed -i "s/^innodb_force_recovery=6/#innodb_force_recovery=6/g" $MYSQL_CON_FILE

${SHUTDOWN_MYSQL}
sleep 10;
$STARTUP_MYSQL &


第二个脚本是加载数据的脚本,原理相对简单,就是传输表空间。脚本为load_data.sh

SOURCE_DUMP_PATH = '/tmp'
RECOVER_TABLE_NAME = 'userscore_255'
MYSQL_DATA_DIR = / data / mysql_5727 / data
MYSQL_LOG_DIR = / data / mysql_5727 / log / mysql.err
MYSQL_CONN_STR = "/usr/local/mysql/bin/mysql --socket=/data/mysql_5727/tmp/mysql.sock --port=5727 -Ne"
MYSQL_RECOVER_DB = test_recover
MYSQL_CON_FILE = / data / mysql_5727 / my.cnf
SHUTDOWN_MYSQL = "mysqladmin shutdown --socket=/data/mysql_5727/tmp/mysql.sock --port=5727"
STARTUP_MYSQL = "/bin/sh /usr/local/Percona-Server-5.7.16-10-Linux.x86_64.ssl101/bin/mysqld_safe --defaults-file=/data/mysql_5727/my.cnf "

${MYSQL_CONN_STR}
"show create table ${MYSQL_RECOVER_DB}.${RECOVER_TABLE_NAME} \G" | grep - v
"\*" >$SOURCE_DUMP_PATH / ddl_${RECOVER_TABLE_NAME}.sql
sed - i
'1d' $SOURCE_DUMP_PATH / ddl_${RECOVER_TABLE_NAME}.sql
${MYSQL_CONN_STR}
"use ${MYSQL_RECOVER_DB};drop table ${RECOVER_TABLE_NAME};"
${MYSQL_CONN_STR}
"use ${MYSQL_RECOVER_DB};source $SOURCE_DUMP_PATH/ddl_${RECOVER_TABLE_NAME}.sql;"

${MYSQL_CONN_STR}
"use ${MYSQL_RECOVER_DB};alter table ${RECOVER_TABLE_NAME} discard tablespace;"

/ bin / cp ${SOURCE_DUMP_PATH} /${RECOVER_TABLE_NAME}.ibd ${MYSQL_DATA_DIR} /${MYSQL_RECOVER_DB} /${
RECOVER_TABLE_NAME}.ibd
chown - R
mysql:mysql ${MYSQL_DATA_DIR} /${MYSQL_RECOVER_DB} /${RECOVER_TABLE_NAME}.ibd

${MYSQL_CONN_STR}
"use ${MYSQL_RECOVER_DB};alter table ${RECOVER_TABLE_NAME} import tablespace;"

${MYSQL_CONN_STR}
"use ${MYSQL_RECOVER_DB};select count(*)from ${RECOVER_TABLE_NAME} ;"


输出的日志如下:

sh get_tab_ddl.sh 

total 11280

-rw-r----- 1 mysql mysql       61 Feb 25 14:49 db.opt

-rw-r----- 1 mysql mysql     8754 Feb 25 23:34 userscore_255.frm

-rw-r----- 1 mysql mysql 11534336 Feb 25 23:34 userscore_255.ibd

+---------------------+

| 2019-02-25 23:34:27 |

+---------------------+

total 11280

-rw-r----- 1 mysql mysql       61 Feb 25 14:49 db.opt

-rw-r----- 1 mysql mysql     8754 Feb 25 23:34 userscore_255.frm

-rw-r----- 1 mysql mysql 11534336 Feb 25 23:34 userscore_255.ibd

*************************** 1. row ***************************

userscore_255

CREATE TABLE `userscore_255` (

  `PID` int(11) NOT NULL,

  `Score` int(11) DEFAULT NULL,

  `Master` int(11) DEFAULT NULL,

  `MCount` int(11) DEFAULT NULL,

  `MTime` int(11) DEFAULT NULL,

  `UDate` int(11) DEFAULT NULL,

  `CDate` int(11) DEFAULT NULL,

  PRIMARY KEY (`PID`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

Generate dynamic columns...7

2019-02-25T15:34:32.593762Z mysqld_safe mysqld from pid file /data/mysql_5727/tmp/mysql.pid ended


sh load_data.sh 

+-------+

| 33213 |

+-------+

只要能够正常读取,我们是mysqldump导出还是做其他的数据流转,都是手到擒来的事情。


640?


猜你喜欢

转载自blog.csdn.net/weixin_36250635/article/details/87927559