无备份情况下恢复MySQL truncate table
本站文章除注明转载外,均为本站原创: 转载自love wife love life —Roger的Oracle/MySQL/PostgreSQL数据恢复博客
本文链接地址: 无备份情况下恢复MySQL truncate table
近期陆续有客户遇到MySQL的数据库问题,最近正好开始研究MySQL的一些技术。大家都知道我之前是擅长Oracle恢复,如果不会MySQL 数据库恢复,总感觉却少一点什么。既然如此,就顺道研究一下吧。幸运的是,Google能够发现一些针对MySQL的恢复文章以及一些工具。
我们都知道,MySQL Server都很多存储引擎,并不是每种都可以进行异常情况之下都恢复,比如drop table/tuncate table/delete table/update table /drop database /又或者是ibdata文件损坏之类的。用的最多的就是Myisam和innodb存储引擎。目前基本上都是5.5+版本了,我想几乎没有人再去使用Myisam了吧。我这里所测试都5.6,5.7版本中默认都存储引擎已经是Innodb了。因此这里我以Innodb引擎为例子进行说明。
首先这里我要利用undrop_for_innodb 这个开源工具包(当然需要编译),目前该工具已经在2017年1月宣布闭源了,而且开始收费。但是我们仍然开源使用之前都开源工具包。另外这里可以告诉大家,不久的将来,odu 也会支持MySQL.
如下是我的truncate table 测试过程:
1. 创建测试表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
mysql> set global innodb_file_per_table=on; Query OK, 0 rows affected (0.00 sec) mysql> show global variables like '%file_per%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_file_per_table | ON | +-----------------------+-------+ 1 row in set (0.00 sec) mysql> use recover; Database changed mysql> create table t_enmotech(a int); Query OK, 0 rows affected (0.01 sec) mysql> insert into t_enmotech values('9999'); Query OK, 1 row affected (0.00 sec) mysql> alter table t_enmotech add primary key(a); Query OK, 1 row affected (0.01 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> explain select * from t_enmotech where a=9999 ; +----+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+ | 1 | SIMPLE | t_enmotech | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | +----+-------------+------------+-------+---------------+---------+---------+-------+------+-------------+ 1 row in set (0.00 sec) |
2、备份表结构
1 2 |
[root@killdb innodb_recovery]# mysqldump --opt -d -uroot -proger recover t_enmotech > /tmp/innodb_recovery/recover/t_enmotech.sql [root@killdb innodb_recovery]# |
3、truncate table
1 2 |
mysql> truncate table t_enmotech; Query OK, 0 rows affected (0.00 sec) |
4、获取数据字典
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@killdb innodb_recovery]# ./stream_parser -f /var/lib/mysql/ibdata1 Opening file: /var/lib/mysql/ibdata1 File information: ID of device containing file: 64768 inode number: 924765 protection: 100660 (regular file) number of hard links: 1 user ID of owner: 496 group ID of owner: 491 device ID (if special file): 0 blocksize for filesystem I/O: 4096 number of blocks allocated: 69632 time of last access: 1496412155 Fri Jun 2 22:02:35 2017 time of last modification: 1496416863 Fri Jun 2 23:21:03 2017 time of last status change: 1496416863 Fri Jun 2 23:21:03 2017 total size, in bytes: 35651584 (34.000 MiB) Size to process: 35651584 (34.000 MiB) All workers finished in 0 sec |
5、扫描逻辑卷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
[root@killdb innodb_recovery]# ./stream_parser -f /dev/mapper/vg_oel6-lv_root -t 36000000k Opening file: /dev/mapper/vg_oel6-lv_root File information: ID of device containing file: 5 inode number: 6307 protection: 60660 (block device) number of hard links: 1 user ID of owner: 0 group ID of owner: 6 device ID (if special file): 64768 blocksize for filesystem I/O: 4096 number of blocks allocated: 0 time of last access: 1496411556 Fri Jun 2 21:52:36 2017 time of last modification: 1496113795 Tue May 30 11:09:55 2017 time of last status change: 1496113795 Tue May 30 11:09:55 2017 total size, in bytes: 0 (0.000 exp(+0)) Size to process: 36864000000 (34.332 GiB) Worker(0): 1.04% done. 2017-06-02 23:26:25 ETA(in 00:04:50). Processing speed: 119.792 MiB/sec Worker(0): 2.07% done. 2017-06-02 23:26:25 ETA(in 00:04:47). Processing speed: 119.767 MiB/sec Worker(0): 3.09% done. 2017-06-02 23:26:25 ETA(in 00:04:44). Processing speed: 119.767 MiB/sec Worker(0): 4.11% done. 2017-06-02 23:26:25 ETA(in 00:04:41). Processing speed: 119.773 MiB/sec Worker(0): 5.13% done. 2017-06-02 23:26:25 ETA(in 00:04:38). Processing speed: 119.773 MiB/sec Worker(0): 6.16% done. 2017-06-02 23:26:25 ETA(in 00:04:35). Processing speed: 119.787 MiB/sec Worker(0): 7.18% done. 2017-06-02 23:26:25 ETA(in 00:04:32). Processing speed: 119.767 MiB/sec Worker(0): 8.20% done. 2017-06-02 23:27:56 ETA(in 00:05:59). Processing speed: 89.829 MiB/sec Worker(0): 9.22% done. 2017-06-02 23:26:26 ETA(in 00:04:26). Processing speed: 119.776 MiB/sec Worker(0): 10.24% done. 2017-06-02 23:26:26 ETA(in 00:04:23). Processing speed: 119.773 MiB/sec ...... Worker(0): 96.10% done. 2017-06-02 23:26:36 ETA(in 00:00:11). Processing speed: 119.768 MiB/sec Worker(0): 97.12% done. 2017-06-02 23:26:36 ETA(in 00:00:08). Processing speed: 119.771 MiB/sec Worker(0): 98.14% done. 2017-06-02 23:26:36 ETA(in 00:00:05). Processing speed: 119.771 MiB/sec Worker(0): 99.17% done. 2017-06-02 23:26:36 ETA(in 00:00:02). Processing speed: 119.784 MiB/sec All workers finished in 306 sec [root@killdb innodb_recovery]# |
6、创建数据字典表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[root@killdb innodb_recovery]# ./recover_dictionary.sh Generating dictionary tables dumps... OK Creating test database ... OK Creating dictionary tables in database test: SYS_TABLES ... OK SYS_COLUMNS ... OK SYS_INDEXES ... OK SYS_FIELDS ... OK All OK Loading dictionary tables data: SYS_TABLES ... 150 recs OK SYS_COLUMNS ... 243 recs OK SYS_INDEXES ... 120 recs OK SYS_FIELDS ... 122 recs OK All OK |
该工具包提供的recover_dictionary脚本会创建一个test数据库,并创建一些数据字典表供恢复查询使用。同时也会在当前目录创建dictionary目录,该目录下会存放数据字典信息。
7、查询需要恢复的表的index_id信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
mysql> use test; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from SYS_TABLES where name like 'recover/t_enmotech%'; +--------------------+-----+--------+------+--------+---------+--------------+-------+ | NAME | ID | N_COLS | TYPE | MIX_ID | MIX_LEN | CLUSTER_NAME | SPACE | +--------------------+-----+--------+------+--------+---------+--------------+-------+ | recover/t_enmotech | 181 | 1 | 1 | 0 | 0 | | 0 | +--------------------+-----+--------+------+--------+---------+--------------+-------+ 1 row in set (0.00 sec) mysql> select * from SYS_INDEXES where table_id=181; +----------+-----+---------+----------+------+-------+---------+ | TABLE_ID | ID | NAME | N_FIELDS | TYPE | SPACE | PAGE_NO | +----------+-----+---------+----------+------+-------+---------+ | 181 | 178 | PRIMARY | 1 | 3 | 0 | 552 | +----------+-----+---------+----------+------+-------+---------+ 1 row in set (0.00 sec) |
可以看到被truncate的表的index_id 为178,我们应该进一步从178 的page中获取数据。
8、确认数据是否存在
1 2 3 4 5 6 7 8 9 10 11 12 |
[root@killdb innodb_recovery]# ./c_parser -6f pages-vg_oel6-lv_root/FIL_PAGE_INDEX/0000000000000178.page -t recover/t_enmotech.sql |head -10 -- Page id: 552, Format: COMPACT, Records list: Valid, Expected records: (0 0) -- Page id: 552, Found records: 0, Lost records: NO, Leaf page: YES -- Page id: 552, Format: COMPACT, Records list: Valid, Expected records: (0 0) -- Page id: 552, Found records: 0, Lost records: NO, Leaf page: YES -- Page id: 557, Format: COMPACT, Records list: Valid, Expected records: (1 1) 000000001306 870000013F0110t_enmotech 9999 -- Page id: 557, Found records: 1, Lost records: NO, Leaf page: YES -- Page id: 552, Format: COMPACT, Records list: Valid, Expected records: (0 0) -- Page id: 552, Found records: 0, Lost records: NO, Leaf page: YES -- Page id: 552, Format: COMPACT, Records list: Valid, Expected records: (0 0) [root@killdb innodb_recovery]# |
9、抽取page中的数据
1 2 3 4 5 6 |
[root@killdb innodb_recovery]# ./c_parser -6f pages-vg_oel6-lv_root/FIL_PAGE_INDEX/0000000000000178.page -t recover/t_enmotech.sql > dumps/default/t_enmotech 2> dumps/default/t_enmotech.sql [root@killdb innodb_recovery]# [root@killdb innodb_recovery]# ls -ltr dumps/default/t_enmotech* -rw-r--r--. 1 root root 222 Jun 3 06:04 dumps/default/t_enmotech.sql -rw-r--r--. 1 root root 1455 Jun 3 06:04 dumps/default/t_enmotech |
抽取数据之前,必须提前准备好表的表结构,由于这里是truncate,因此表结构是存在的,很容易获取。我这里是测试,所以之前就备份了结构。
那么如果是drop table 呢? 实际上我们也可以通过该工具来恢复表结构。
10、加载数据到mysql server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
mysql> use recover; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +-------------------+ | Tables_in_recover | +-------------------+ | t_enmotech | | t_recover | | test_0731 | | test_drop | +-------------------+ 4 rows in set (0.00 sec) mysql> source dumps/default/t_enmotech.sql Query OK, 0 rows affected (0.00 sec) Query OK, 3 rows affected (0.00 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 |
11、验证数据
1 2 3 4 5 6 7 |
mysql> select * from t_enmotech; +------+ | a | +------+ | 9999 | +------+ 1 row in set (0.00 sec) |
我们可以看到,被truncate 掉的数据被成功恢复了回来。
这里我测试的truncate table的场景,其实对于drop table、delete table 恢复方法均类似(已测试过)。另外,对于更为严重的drop database 其实也是可以进行恢复的。
当然,对于实际的生产库来讲,数据不一定能够恢复,因为有可能被覆盖而导致数据恢复不全。MySQL 对于空间的重用机制与Oracle 有很大区别,对于Oracle 而言,如果是delete的数据,还是很难被覆盖掉的,对于drop 和truncate 则领导别论。然而MySQL则有所不同,MySQL 默认会启动一些purge 进程来进行空间重用,这是MySQL 5.6的情况:
1 2 3 4 5 6 7 8 9 10 |
mysql> show global variables like '%purge%'; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | innodb_max_purge_lag | 0 | | innodb_purge_batch_size | 20 | | innodb_purge_threads | 0 | | relay_log_purge | ON | +-------------------------+-------+ 4 rows in set (0.00 sec) |
在MySQL 5.7 版本中更为坑爹,MySQL 默认会启动4个purge 线程,因此很容易就会导致空间被重用,最终导致数据无法恢复,如下是MySQL 5.7的purge相关参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> show global variables like '%purge%'; +--------------------------------------+-------+ | Variable_name | Value | +--------------------------------------+-------+ | gtid_purged | | | innodb_max_purge_lag | 0 | | innodb_max_purge_lag_delay | 0 | | innodb_purge_batch_size | 300 | | innodb_purge_rseg_truncate_frequency | 128 | | innodb_purge_threads | 4 | | relay_log_purge | ON | +--------------------------------------+-------+ 7 rows in set (0.01 sec) |
因此,一旦你遭遇turncate table/drop table/delete /drop database等情况,建议立刻停止服务或者停止数据库,保留现场,以防止环境进一步恶化,最终导致数据无法恢复的情况出现。
云和恩墨,汇集了国内专业的Oracle、MySQL等数据恢复专家,愿为你的数据库保驾护航!
Leave a Reply
You must be logged in to post a comment.