当我们意外地基于MySQL运行错误的查询时该怎么办?是否可以从MySQL恢复已删除的数据?如果我们使用binlog,例如我们在MySQL复制中使用的binlog,我们可以对此做一些事情。
您正在查看的文章超过2年。请记住,其中的某些信息可能已经过时。。
为了从MySQL恢复数据,我们将使用以ROW模式运行的宾果游戏。如果我们删除了一条记录但未启用它,则将无法恢复丢失的数据。在中/etc/my.cnf
,应设置以下参数:
binlog-format = ROW
log-bin = /var/log/mysql/bin-log
我们将创建一个测试数据库并用记录完成它。
CREATE DATABASE `undelete`;
USE `undelete`;
CREATE TABLE `names` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO `names` (name) VALUES ("Kamil"), ("Michal"), ("Adam"), ("Gerard"), ("Bartek");
我们创建了一个带有名称表的未删除数据库,该表包含五个记录。可以肯定的是,让我们检查其内容:
mysql> select * from names;
+----+---------------------+--------+
| id | date | name |
+----+---------------------+--------+
| 1 | 2015-02-14 09:12:42 | Kamil |
| 2 | 2015-02-14 09:12:42 | Michal |
| 3 | 2015-02-14 09:12:42 | Adam |
| 4 | 2015-02-14 09:12:42 | Gerard |
| 5 | 2015-02-14 09:12:42 | Bartek |
+----+---------------------+--------+
5 rows in set (0.00 sec)
我们将从数据库中删除随机记录,然后尝试恢复它们。
mysql> DELETE FROM `names` WHERE id >3;
Query OK, 2 rows affected (0.00 sec)
mysql> select * from names;
+----+---------------------+--------+
| id | date | name |
+----+---------------------+--------+
| 1 | 2015-02-14 09:12:42 | Kamil |
| 2 | 2015-02-14 09:12:42 | Michal |
| 3 | 2015-02-14 09:12:42 | Adam |
+----+---------------------+--------+
3 rows in set (0.00 sec)
正如您在上面看到的,我们设法删除了两条记录。此信息应保存在我们将从中检索数据的宾果游戏中。在我们的例子中,它是一个bin-log.000001文件。
mysql> show binlog events in 'bin-log.000001';
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
| bin-log.000001 | 4 | Format_desc | 1 | 120 | Server ver: 5.6.22-log, Binlog ver: 4 |
| bin-log.000001 | 120 | Query | 1 | 196 | BEGIN |
| bin-log.000001 | 196 | Table_map | 1 | 253 | table_id: 73 (undelete.names) |
| bin-log.000001 | 253 | Delete_rows | 1 | 320 | table_id: 73 flags: STMT_END_F |
| bin-log.000001 | 320 | Xid | 1 | 351 | COMMIT /* xid=78 */ |
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
Spistreści
用mysqlbinlog恢复
可以使用mysqlbinlog命令手动执行数据恢复操作。它使我们能够怀疑存储在二进制日志中的数据。
mysqlbinlog -v --base64-output=DECODE-ROWS --start-position=196 /var/log/mysql/bin-log.000001
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 196
#150215 9:36:32 server id 1 end_log_pos 253 CRC32 0x6bedb0e7 Table_map: `undelete`.`names` mapped to number 73
# at 253
#150215 9:36:32 server id 1 end_log_pos 320 CRC32 0x1bc278f9 Delete_rows: table id 73 flags: STMT_END_F
### DELETE FROM `undelete`.`names`
### WHERE
### @1=4
### @2=1423992931
### @3='Gerard'
### DELETE FROM `undelete`.`names`
### WHERE
### @1=5
### @2=1423992931
### @3='Bartek'
# at 320
#150215 9:36:32 server id 1 end_log_pos 351 CRC32 0xee833dd0 Xid = 78
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
如果没有太多数据,我们可以将它们手动移动到数据库中。如果数量很多,我们可以尝试使用MyUndelete之类的脚本。
使用MyUndelete恢复
下载脚本https://download.csdn.net/download/bill20100829/15402393。我们还需要修改文件vim〜/ .my.cnf,其中包含用于登录数据库的数据。
在我们的例子中,数据删除是在binog 120和351中的位置之间发生的。因此,我们发出了一条适当的命令,希望可以恢复数据:
./MyUndelete.py -b /var/log/mysql/bin-log.000001 -s 120 -e 351
*** WARNING *** USE WITH CARE ****
Binlog file is /var/log/mysql/bin-log.000001
Start Position file is 120
End Postision file is 351
Event type (' ') is a delete v2
Ready to revert the statement ? [y/n]
y
Done... I hope it worked ;)
现在,我们可以在数据库中检查结果。如下所示,数据已还原。
<span style="color:#475978"><code>mysql> use undelete;
Database changed
mysql> select * from names;
+----+---------------------+--------+
| id | date | name |
+----+---------------------+--------+
| 1 | 2015-02-15 09:35:31 | Kamil |
| 2 | 2015-02-15 09:35:31 | Michal |
| 3 | 2015-02-15 09:35:31 | Adam |
| 4 | 2015-02-15 09:35:31 | Gerard |
| 5 | 2015-02-15 09:35:31 | Bartek |
+----+---------------------+--------+
5 rows in set (0.00 sec)</code></span>
该脚本还允许在基于错误的INSERT和UPDATE命令执行的情况下撤消数据。
语法
MyUndelete.py -b <binlog> -s <start position> -e <end position> [-i] [-u]
-b | --binlog= : path of the binary log file
-s | --start= : start position
-e | --end= : stop position
-i | --insert : consider also INSERT statements (by default, only DELETE)
-u | --update : consider also UPDATE statements (by default, only DELETE)
-d | --debug : add debug messages
Info: The program expects that you have read access to the binary log
and you have all eventual MySQL credential in ~/.my.cnf
例子
取消删除
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
| 4 | Héloïse |
| 5 | Suhi |
+----+------------+
5 rows in set (0.00 sec)
mysql> delete from fred where id >3;
Query OK, 2 rows affected (0.04 sec)
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
+----+------------+
3 rows in set (0.00 sec)
mysql> show binlog events in 'mysql-bin.000008';
+------------------+------+-------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+-------------+-----------+-------------+---------------------------------------+
...
| mysql-bin.000008 | 1514 | Query | 1 | 1586 | BEGIN |
| mysql-bin.000008 | 1586 | Table_map | 1 | 1636 | table_id: 72 (fred.fred) |
| mysql-bin.000008 | 1636 | Delete_rows | 1 | 1694 | table_id: 72 flags: STMT_END_F |
| mysql-bin.000008 | 1694 | Xid | 1 | 1725 | COMMIT /* xid=146 */ |
+------------------+------+-------------+-----------+-------------+---------------------------------------+
$ sudo ./MyUndelete.py -b /var/lib/mysql/mysql-bin.000008 -s 1514 -e 1725
*** WARNING *** USE WITH CARE ****
Binlog file is /var/lib/mysql/mysql-bin.000008
Start Position file is 1514
End Postision file is 1725
Event type (' ') is a delete v2
Ready to revert the statement ? [y/n]
y
Done... I hope it worked ;)
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
| 4 | Héloïse |
| 5 | Suhi |
+----+------------+
5 rows in set (0.00 sec)
取消插入
删除在positon 41989和42207之间的二进制日志mysqld-bin.000004中发生的插入。
$ sudo ./MyUndelete.py -s 41989 -e 42207 -i -b /var/lib/mysql/mysqld-bin.000004
*** WARNING *** USE WITH CARE ****
Binlog file is /var/lib/mysql/mysqld-bin.000004
Start Position file is 41989
End Postision file is 42207
We also look to undo INSERTs
Event type ('\x1e') is an insert v2
Ready to revert the statement ? [y/n]
y
Done... I hope it worked ;)
取消更新
让我们修改一些记录,然后还原更改:
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
| 4 | Héloïse |
| 5 | Suhi |
+----+------------+
mysql> update fred set name = concat(name, "2") where id >3;
Query OK, 2 rows affected (0.03 sec)
Rows matched: 2 Changed: 2 Warnings: 0
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
| 4 | Héloïse2 |
| 5 | Suhi2 |
+----+------------+
5 rows in set (0.00 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000008 | 357 | | | |
+------------------+----------+--------------+------------------+-------------------+
mysql> show binlog events in 'mysql-bin.000008';
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
| mysql-bin.000008 | 4 | Format_desc | 1 | 120 | Server ver: 5.6.21-log, Binlog ver: 4 |
| mysql-bin.000008 | 120 | Query | 1 | 192 | BEGIN |
| mysql-bin.000008 | 192 | Table_map | 1 | 242 | table_id: 72 (fred.fred) |
| mysql-bin.000008 | 242 | Update_rows | 1 | 326 | table_id: 72 flags: STMT_END_F |
| mysql-bin.000008 | 326 | Xid | 1 | 357 | COMMIT /* xid=22 */ |
+------------------+-----+-------------+-----------+-------------+---------------------------------------+
5 rows in set (0.00 sec)
$ sudo ./MyUndelete.py -b /var/lib/mysql/mysql-bin.000008 -s 120 -e 357 -u
*** WARNING *** USE WITH CARE ****
Binlog file is /var/lib/mysql/mysql-bin.000008
Start Position file is 120
End Postision file is 357
Event type ('\x1f') is an update v2
We got an update!!
Ready to revert the statement ? [y/n]
y
Sending to mysql...
Done... I hope it worked ;)
mysql> select * from fred;
+----+------------+
| id | name |
+----+------------+
| 1 | Fred |
| 2 | Jen |
| 3 | Wilhelmine |
| 4 | Héloïse |
| 5 | Suhi |
+----+------------+
5 rows in set (0.00 sec)