当前位置:   article > 正文

没有备份怎么恢复被drop的表(利用undrop-for-innodb)

widdows怎么使用undrop-for-innodb恢复drop table数据

介绍:

    也许大家都难以理解,这么重要的数据为啥不备份(或者备份不可用)?而且还任性的drop table了。显然有备份是最好的,但是它们并不总是可用的。这种情况令人恐惧,但并非毫无希望。在许多情况下,可以恢复数据库或表中的几乎所有数据。恢复计划取决于InnoDB是否将所有数据保存在一个ibdata1中,还是每个表都有自己的表空间。本文将考虑innodb_file_per_table=OFF的情况。

drop 表恢复其他方法:

本文重要部分:

环境:

  • 时间:#Sat Aug 4 19:37:24 CST 2018
  • CentOS Linux release 7.2.1511 (Core)
  • MySQL:5.7.23-log
  • 使用工具:undrop-for-innodb
1.工具安装
  • 依赖包安装
yum install -y make gcc flex bison
  • 下载工具包(github)
  1. #cd /opt/
  2. [root@db13_19:55:25 /opt]
  3. #git clone https://github.com/twindb/undrop-for-innodb.git
  4. Cloning into 'undrop-for-innodb'...
  5. Resolving deltas: 100% (77/77), done.
  • make
  1. #cd /opt/undrop-for-innodb
  2. #make
  3. ....
  4. ....
  5. [root@db13_20:39:43 /opt/undrop-for-innodb]
  6. #ll
  7. total 2920
  8. -rw-r--r-- 1 root root 6271 Aug 4 19:55 check_data.c
  9. -rw-r--r-- 1 root root 66128 Aug 4 20:39 check_data.o
  10. -rwxr-xr-x 1 root root 727801 Aug 4 20:39 c_parser
  11. -rw-r--r-- 1 root root 28587 Aug 4 19:55 c_parser.c
  12. -rw-r--r-- 1 root root 1030296 Aug 4 20:39 c_parser.o
  13. drwxr-xr-x 2 root root 92 Aug 4 19:55 dictionary
  14. -rw-r--r-- 1 root root 1978 Aug 4 19:55 fetch_data.sh
  15. drwxr-xr-x 2 root root 4096 Aug 4 19:55 include
  16. -rw-r--r-- 1 root root 8936 Aug 4 19:55 innochecksum.c
  17. -rwxr-xr-x 1 root root 36343 Aug 4 20:39 innochecksum_changer
  18. -rw-r--r-- 1 root root 154459 Aug 4 20:39 lex.yy.c
  19. -rw-r--r-- 1 root root 18047 Aug 4 19:55 LICENSE
  20. -rw-r--r-- 1 root root 1942 Aug 4 19:55 Makefile
  21. -rw-r--r-- 1 root root 16585 Aug 4 19:55 print_data.c
  22. -rw-r--r-- 1 root root 127176 Aug 4 20:39 print_data.o
  23. -rw-r--r-- 1 root root 3464 Aug 4 19:55 README.md
  24. -rwxr-xr-x 1 root root 1536 Aug 4 19:55 recover_dictionary.sh
  25. drwxr-xr-x 2 root root 4096 Aug 4 19:55 sakila
  26. -rw-r--r-- 1 root root 103506 Aug 4 20:39 sql_parser.c
  27. -rw-r--r-- 1 root root 8462 Aug 4 19:55 sql_parser.l
  28. -rw-r--r-- 1 root root 296840 Aug 4 20:39 sql_parser.o
  29. -rw-r--r-- 1 root root 26355 Aug 4 19:55 sql_parser.y
  30. -rwxr-xr-x 1 root root 61725 Aug 4 20:39 stream_parser
  31. -rw-r--r-- 1 root root 23103 Aug 4 19:55 stream_parser.c
  32. -rw-r--r-- 1 root root 109304 Aug 4 20:39 stream_parser.o
  33. -rw-r--r-- 1 root root 14764 Aug 4 19:55 sys_parser.c
  34. -rw-r--r-- 1 root root 2182 Aug 4 19:55 tables_dict.c
  35. -rw-r--r-- 1 root root 40264 Aug 4 20:39 tables_dict.o
  36. -rwxr-xr-x 1 root root 6629 Aug 4 19:55 test.sh
  37. drwxr-xr-x 3 root root 42 Aug 4 19:55 vagrant
  38. [root@db13_20:39:57 /opt/undrop-for-innodb]

  • 增加用于恢复表结构的工具sys_parser(官方文档未使用):
  1. #gcc `/usr/local/mysql57/bin/mysql_config --cflags` `/usr/local/mysql57/bin/mysql_config --libs` -o sys_parser sys_parser.c
  2. 注:mysql_basedir: /usr/local/mysql57/bin/

2.表数据生成和drop表

  • 初始化一个新实例:
  1. [root@db212_20:58:44 /data/57mysql]
  2. #mkdir mysql3507/{data,logs,tmp} -p
  3. #chown -R mysql:mysql mysql3507
  4. [root@db212_21:07:34 /3507]
  5. \\复制my3507.cnf到mysql3507下
  6. #ln -s /data/57mysql/mysql3507/ /3507
  7. #/usr/local/mysql57/bin/mysqld --defaults-file=/3507/my3507.cnf --initialize-insecure
  8. #/usr/local/mysql57/bin/mysqld --defaults-file=/3507/my3507.cnf&
  9. [1] 11669
  10. //取初始密码并登录:
  11. /usr/local/mysql57/bin/mysql -S /tmp/mysql3507.sock -uroot
  1. (unknown)@localhost [(none)]>alter user user() identified by '*****';
  2. root@localhost [(none)]>CREATE DATABASE wenyz;
  3. root@localhost [(none)]>use wenyz;
  4. Database changed
  5. //创建表
  6. root@localhost [wenyz]>CREATE TABLE `t2` (
  7. -> `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  8. -> `ti` varchar(100) NOT NULL,
  9. -> `date` date DEFAULT NULL,
  10. -> PRIMARY KEY (`id`)
  11. -> ) ENGINE=InnoDB AUTO_INCREMENT=4079859 DEFAULT CHARSET=utf8;
  12. Query OK, 0 rows affected (0.01 sec)
  13. //造数据
  14. root@localhost [wenyz]>insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  15. Records: 448 Duplicates: 0 Warnings: 0
  16. //查看数据行数和checksum值
  17. root@localhost [wenyz]>select count(*) from t2;
  18. +----------+
  19. | count(*) |
  20. +----------+
  21. | 896 |
  22. +----------+
  23. root@localhost [wenyz]>checksum table t2;
  24. +----------+------------+
  25. | Table | Checksum |
  26. +----------+------------+
  27. | wenyz.t2 | 3458542072 |
  28. +----------+------------+
  29. //DROP
  30. root@localhost [wenyz]>drop table t2;
  31. Query OK, 0 rows affected (0.01 sec)

3.利用stream_parser将ibdata1文件导出成page文件

  1. #cd /opt/undrop-for-innodb/
  2. [root@db212_21:25:52 /opt/undrop-for-innodb]
  3. #./stream_parser -f /3507/data/ibdata1
  4. Opening file: /3507/data/ibdata1
  5. File information:
  6. ....
  7. Size to process: 104857600 (100.000 MiB)
  8. Size to process: 104857600 (100.000 MiB)
  9. time of last access: 1533388916 Sat Aug 4 21:21:56 2018
  10. time of last modification: 1533388917 Sat Aug 4 21:21:57 2018
  11. time of last status change: 1533388917 Sat Aug 4 21:21:57 2018
  12. total size, in bytes: 104857600 (100.000 MiB)
  13. Size to process: 104857600 (100.000 MiB)
  14. All workers finished in 0 sec

4.恢复表结构 [ Top ]

这里引入官方的一段描述:

InnoDB stores all data in B+tree indexes. A table has one clustered index PRIMARY, all fields are stored there. Thus, if the table has secondary keys, each key has an index. Each index is identified by index_id.

Consequently, if we want to recover a table, we have to find all pages that belong to a particular index_id.

  • 接下来我们先来看看怎么找到table id和INDEX_ID(page文件编号)的,理解原理后再用程序跑一次,导入到临时数据库中

  • 手工查找table id
    观察以下结果中,wenyz/t2后的40,即为table id
  1. [root@db212_21:25:55 /opt/undrop-for-innodb]
  2. #./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000001.page -t dictionary/SYS_TABLES.sql | grep 'wenyz/t2'
  3. 000000000521 3B00000149047E SYS_TABLES "wenyz/t2" 40 3 33 0 64 "" 0
  4. SET FOREIGN_KEY_CHECKS=0;
  5. LOAD DATA LOCAL INFILE '000000000521 3B00000149047E SYS_TABLES "wenyz/t2" 40 3 33 0 64 "" 0
  • 通过table id查看page文件编号
    观察以下结果中40后的41,即为INDEX_ID(page文件编号)
  1. [root@db212_21:29:54 /opt/undrop-for-innodb]
  2. #./c_parser -4Df pages-ibdata1/FIL_PAGE_INDEX/0000000000000003.page -t dictionary/SYS_INDEXES.sql | grep '40'
  3. 000000000521 3B0000014903A2 SYS_INDEXES 40 41 "PRIMARY" 1 3 0 4294967295
  4. SET FOREIGN_KEY_CHECKS=0;
  5. 000000000521 3B0000014903A2 SYS_INDEXES 40 41 "PRIMARY" 1 3 0 4294967295
  • 用程序恢复字典信息:编辑mysql登录信息.
  1. [root@db212_21:57:04 /opt/undrop-for-innodb]
  2. //将文件中三处mysql替换成/usr/local/mysql57/bin/mysql --login-path=p3507
  3. #vi recover_dictionary.sh
  4. 43 /usr/local/mysql57/bin/mysql --login-path=p3507 -e "CREATE DATABASE IF NOT EXISTS test"
  5. ...
  6. 50 /usr/local/mysql57/bin/mysql --login-path=p3507 test < dictionary/$t.sql
  7. ...
  8. 58 /usr/local/mysql57/bin/mysql --login-path=p3507 test < dumps/default/$t.sql
  • 执行/recover_dictionary.sh,恢复字典信息
  1. [root@db212_22:13:57 /opt/undrop-for-innodb]
  2. #./recover_dictionary.sh
  3. Generating dictionary tables dumps... OK
  4. Creating test database ... OK
  5. Creating dictionary tables in database test:
  6. SYS_TABLES ... OK
  7. SYS_COLUMNS ... OK
  8. SYS_INDEXES ... OK
  9. SYS_FIELDS ... OK
  10. All OK
  11. Loading dictionary tables data:
  12. SYS_TABLES ... 52 recs OK
  13. SYS_COLUMNS ... 284 recs OK
  14. SYS_INDEXES ... 68 recs OK
  15. SYS_FIELDS ... 90 recs OK
  16. All OK
  17. [root@db212_22:14:02 /opt/undrop-for-innodb]
  • 登录mysql查看信息字典信息:
  1. mysql --login-path=p3507
  2. root@localhost [(none)]>use test;
  3. Database changed
  4. root@localhost [test]>select * from SYS_TABLES where name like 'wenyz/t2%';
  5. +----------+----+--------+------+--------+---------+--------------+-------+
  6. | NAME | ID | N_COLS | TYPE | MIX_ID | MIX_LEN | CLUSTER_NAME | SPACE |
  7. +----------+----+--------+------+--------+---------+--------------+-------+
  8. | wenyz/t2 | 40 | 3 | 33 | 0 | 64 | | 0 |
  9. +----------+----+--------+------+--------+---------+--------------+-------+
  10. 1 row in set (0.00 sec)
  11. root@localhost [test]>select * from SYS_INDEXES where table_id=40;
  12. +----------+----+---------+----------+------+-------+------------+
  13. | TABLE_ID | ID | NAME | N_FIELDS | TYPE | SPACE | PAGE_NO |
  14. +----------+----+---------+----------+------+-------+------------+
  15. | 40 | 41 | PRIMARY | 1 | 3 | 0 | 4294967295 |
  16. +----------+----+---------+----------+------+-------+------------+
  17. //注意记录上表中id,此ID为INDEX_ID(page文件编号)等会表数据恢复要使用
  18. 1 row in set (0.00 sec)
  19. root@localhost [test]>
  1. ./sys_parser -h 127.0.0.1 -u root -p xxxx -d test wenyz/t2
  2. ./sys_parser: error while loading shared libraries: libmysqlclient.so.20: cannot open shared object file: No such file or directory
  3. #ln -s /opt/mysql-5.7.23-linux-glibc2.12-x86_64/lib/libmysqlclient.so.20 /usr/lib64/libmysqlclient.so.20
  4. [root@db212_22:23:25 /opt/undrop-for-innodb]
  5. #./sys_parser -h 127.0.0.1 -u root -p zstzst -d test wenyz/t2
  6. CREATE TABLE `t2`(
  7. `id` INT UNSIGNED NOT NULL,
  8. `ti` VARCHAR(100) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL,
  9. `date` DATE,
  10. PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB;
  12. [root@db212_22:23:30 /opt/undrop-for-innodb]
  • 将恢复的表结构存到/tmp/t2.sql
  1. #cat /tmp/t2.sql
  2. CREATE TABLE `t2`(
  3. `id` INT UNSIGNED NOT NULL,
  4. `ti` VARCHAR(100) CHARACTER SET 'utf8' COLLATE 'utf8_general_ci' NOT NULL,
  5. `date` DATE,
  6. PRIMARY KEY (`id`)
  7. ) ENGINE=InnoDB;
  8. [root@db212_22:26:20 /opt/undrop-for-innodb]

5.表数据恢复 [ Top ]

  • 查看数据是否存在

以下命令中使用的0000000000000041.page为上面提到的INDEX_ID(page文件编号),/tmp/t2.sql为上面恢复的表结构.

  1. [root@db212_22:33:44 /opt/undrop-for-innodb]
  2. #./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/0000000000000041.page -t /tmp/t2.sql |head -2
  3. -- Page id: 459, Format: COMPACT, Records list: Valid, Expected records: (180 180)
  4. 000000000507 A7000001210110 t2 4079859 "d553635af1a3b" "2018-08-04"
  5. 000000000508 A8000001230110 t2 4079860 "44d64b99fc30d1b" "2018-08-04"
  6. [root@db212_22:33:44 /opt/undrop-for-innodb]
  • 利用c_parser将0000000000000041.page导出成可执行sql
  1. //注意:此处几个文件名程序把导出的两个数据文件的文件名关系是写死的,以下dumps/default/t2中的t2是需要和表名一致,在t2_load.sql中会引用此文件路经.
  2. ./c_parser -6f pages-ibdata1/FIL_PAGE_INDEX/0000000000000041.page -t /tmp/t2.sql > dumps/default/t2 2> dumps/default/t2_load.sql
  3. #cd dumps/default/
  4. [root@db212_22:41:01 /opt/undrop-for-innodb/dumps/default]
  5. #ll
  6. total 132
  7. -rw-r--r-- 1 root root 21232 Aug 4 22:14 SYS_COLUMNS
  8. ...
  9. -rw-r--r-- 1 root root 62923 Aug 4 22:40 t2
  10. -rw-r--r-- 1 root root 308 Aug 4 22:40 t2_load.sql
  11. [root@db212_22:41:04 /opt/undrop-for-innodb/dumps/default]
  12. #cat t2_load.sql
  13. SET FOREIGN_KEY_CHECKS=0;
  14. LOAD DATA LOCAL INFILE '/opt/undrop-for-innodb/dumps/default/t2' REPLACE INTO TABLE `t2` FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '"' LINES STARTING BY 't2\t' (`id`, `ti`, `date`);
  15. -- STATUS {"records_expected": 896, "records_dumped": 896, "records_lost": false} STATUS END
  • 将表结构t2.sql和表数据t2和t2_load.sql导入数据库
  1. root@localhost [test]>source /tmp/t2.sql
  2. Query OK, 0 rows affected (0.02 sec)
  3. root@localhost [test]>source /opt/undrop-for-innodb/dumps/default/t2_load.sql
  4. Query OK, 0 rows affected (0.00 sec)
  5. Query OK, 896 rows affected (0.01 sec)
  6. Records: 896 Deleted: 0 Skipped: 0 Warnings: 0
  7. root@localhost [test]>select count(*) from t2;
  8. +----------+
  9. | count(*) |
  10. +----------+
  11. | 896 |
  12. +----------+
  13. 1 row in set (0.00 sec)
  14. root@localhost [test]>checksum table t2;
  15. +---------+------------+
  16. | Table | Checksum |
  17. +---------+------------+
  18. | test.t2 | 3458542072 |
  19. +---------+------------+
  20. 1 row in set (0.00 sec)
  21. root@localhost [test]>
  22. //drop前数据信息对比:
  23. root@localhost [wenyz]>select count(*) from t2;
  24. +----------+
  25. | count(*) |
  26. +----------+
  27. | 896 |
  28. +----------+
  29. 1 row in set (0.00 sec)
  30. root@localhost [wenyz]>checksum table t2;
  31. +----------+------------+
  32. | Table | Checksum |
  33. +----------+------------+
  34. | wenyz.t2 | 3458542072 |
  35. +----------+------------+
  36. 1 row in set (0.00 sec)
  • 至此数据已经完全恢复.

6.未解决的问题 [ Top ]

问题1.
innodb(非独立表空间)情况,drop表后,用工具读ibdata1对应数据页文件,如果是这个表大于350行左右的数据,页文件是存在的,但小于350行左右,页文件就不存在(drop前页文件是存在的).这是个什么原因呢
脚本输出信息:

以下为精简信息,完整输出信息请点击下载文本(由于不能上传txt,所以在文件名后面加了sh,下载后请删除.sh):
320行,没有drop记录:
320行,drop
640行,drop

  1. select count(*) from t2
  2. 640
  3. Table Checksum
  4. wenyz.t2 1273189789
  5. ...
  6. -rw-r--r-- 1 root root 16384 Aug 4 23:09 0000000000000040.page
  7. -rw-r--r-- 1 root root 98304 Aug 4 23:09 0000000000000041.page /被drop表空间文件
  8. -rw-r--r-- 1 root root 16384 Aug 4 23:09 18446744069414584320.page
  9. count(*)320(行)
  10. Table Checksum
  11. wenyz.t2 3018070873
  12. ....
  13. -rw-r--r-- 1 root root 16384 Aug 4 23:12 0000000000000038.page
  14. -rw-r--r-- 1 root root 32768 Aug 4 23:12 0000000000000039.page
  15. -rw-r--r-- 1 root root 16384 Aug 4 23:12 0000000000000040.page \\这里缺41.page
  16. -rw-r--r-- 1 root root 16384 Aug 4 23:12 18446744069414584320.page

把drop命令在脚本里注释后(还是320行)

  1. 以下为精简信息,完整输出信息附后:
  2. #/tmp/init3507.sh
  3. BrI?Zu>o=1uN
  4. mysql: [Warning] Using a password on the command line interface can be insecure.
  5. root@localhost [(none)]>alter user user() identified by 'xxxxx';
  6. Query OK, 0 rows affected (0.00 sec)
  7. root@localhost [(none)]>exit
  8. Bye
  9. mysql: [Warning] Using a password on the command line interface can be insecure.
  10. count(*) ---------------------------------------------------------------------------------320行数据
  11. 320
  12. Table Checksum
  13. wenyz.t2 2368041617
  14. ...
  15. -rw-r--r-- 1 root root 32768 Aug 4 23:16 0000000000000039.page
  16. -rw-r--r-- 1 root root 16384 Aug 4 23:16 0000000000000040.page
  17. -rw-r--r-- 1 root root 32768 Aug 4 23:16 0000000000000041.page //320行时不drop是有此表空间文件的
  18. -rw-r--r-- 1 root root 16384 Aug 4 23:16 18446744069414584320.page
  19. [root@db211_23:16:21 /opt/undrop-for-innodb]
  20. #
  • 为了快速测试数据恢复,使用的脚本
    init3507.sh
    /tmp/init3507.sh
  1. pkill mysqld
  2. rm -rf /3507/data/* /3507/logs/*
  3. /usr/local/mysql57/bin/mysqld --defaults-file=/3507/my3507.cnf --initialize-insecure
  4. /usr/local/mysql57/bin/mysqld --defaults-file=/3507/my3507.cnf&
  5. sleep 2
  6. /usr/local/mysql57/bin/mysql -S /tmp/mysql3507.sock -uroot </tmp/create_t2.sql
  7. rm -rf /opt/undrop-for-innodb/pages-ibdata1
  8. #ls -l
  9. cd /opt/undrop-for-innodb/
  10. sleep 5
  11. rm -rf /opt/undrop-for-innodb/pages-ibdata1
  12. cd /opt/undrop-for-innodb/
  13. /opt/undrop-for-innodb/stream_parser -f /3507/data/ibdata1
  14. ls -l /opt/undrop-for-innodb/pages-ibdata1/FIL_PAGE_INDEX

/tmp/create_t2.sql (由于不能上传sql文件,所以只能改为sh)

  1. alter user user() identified by 'xxxx';
  2. CREATE DATABASE wenyz;
  3. use wenyz;
  4. CREATE TABLE `t2` (
  5. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  6. `ti` varchar(100) NOT NULL,
  7. `date` date DEFAULT NULL,
  8. PRIMARY KEY (`id`)
  9. ) ENGINE=InnoDB AUTO_INCREMENT=4079859 DEFAULT CHARSET=utf8;
  10. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  11. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  12. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  13. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  14. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  15. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  16. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  17. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  18. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  19. insert into t2(ti,date) values(substring(MD5(RAND()),floor(RAND()*26)+1,15),now()) ;
  20. insert into t2(ti,date) select ti,date from t2;
  21. insert into t2(ti,date) select ti,date from t2;
  22. insert into t2(ti,date) select ti,date from t2;
  23. insert into t2(ti,date) select ti,date from t2;
  24. insert into t2(ti,date) select ti,date from t2;
  25. select count(*) from t2;
  26. checksum table t2;
  27. drop table t2;

7.参考过的资料 [ Top ]

转载于:https://www.cnblogs.com/2woods/p/9420414.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/909254
推荐阅读
相关标签
  

闽ICP备14008679号