add options: start-datetime, stop-datetime
This commit is contained in:
parent
315621d533
commit
2283302a4e
59
README.md
59
README.md
|
@ -6,7 +6,7 @@ binlog2sql
|
||||||
用途
|
用途
|
||||||
===========
|
===========
|
||||||
|
|
||||||
* 数据回滚
|
* 数据快速回滚(闪回)
|
||||||
* 主从切换后数据不一致的修复
|
* 主从切换后数据不一致的修复
|
||||||
* 从binlog生成标准SQL,带来的衍生功能
|
* 从binlog生成标准SQL,带来的衍生功能
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ binlog2sql
|
||||||
==============
|
==============
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/danfengcao/binlog2sql.git
|
shell> git clone https://github.com/danfengcao/binlog2sql.git
|
||||||
pip install -r requirements.txt
|
shell> pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
使用
|
使用
|
||||||
|
@ -36,7 +36,7 @@ pip install -r requirements.txt
|
||||||
[mysqld]
|
[mysqld]
|
||||||
server-id = 1
|
server-id = 1
|
||||||
log_bin = /var/log/mysql/mysql-bin.log
|
log_bin = /var/log/mysql/mysql-bin.log
|
||||||
max_binlog_size = 1000M
|
max_binlog_size = 100M
|
||||||
binlog-format = row
|
binlog-format = row
|
||||||
|
|
||||||
###基本用法
|
###基本用法
|
||||||
|
@ -44,11 +44,9 @@ pip install -r requirements.txt
|
||||||
**解析出标准SQL**
|
**解析出标准SQL**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -t test3 test4 --start-file='mysql-bin.000002'
|
shell> python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -t test3 test4 --start-file='mysql-bin.000002'
|
||||||
|
|
||||||
输出:
|
输出:
|
||||||
INSERT INTO `test`.`test4`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:10', 'test', 1); #start 185 end 351
|
|
||||||
INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:22', '中文', 3); #start 378 end 543
|
|
||||||
INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 570 end 736
|
INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 570 end 736
|
||||||
UPDATE `test`.`test3` SET `addtime`='2016-12-10 12:00:00', `data`='中文', `id`=3 WHERE `addtime`='2016-12-10 13:03:22' AND `data`='中文' AND `id`=3 LIMIT 1; #start 763 end 954
|
UPDATE `test`.`test3` SET `addtime`='2016-12-10 12:00:00', `data`='中文', `id`=3 WHERE `addtime`='2016-12-10 13:03:22' AND `data`='中文' AND `id`=3 LIMIT 1; #start 763 end 954
|
||||||
DELETE FROM `test`.`test3` WHERE `addtime`='2016-12-10 13:03:38' AND `data`='english' AND `id`=4 LIMIT 1; #start 981 end 1147
|
DELETE FROM `test`.`test3` WHERE `addtime`='2016-12-10 13:03:38' AND `data`='english' AND `id`=4 LIMIT 1; #start 981 end 1147
|
||||||
|
@ -57,7 +55,7 @@ DELETE FROM `test`.`test3` WHERE `addtime`='2016-12-10 13:03:38' AND `data`='eng
|
||||||
**解析出回滚SQL**
|
**解析出回滚SQL**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttest3 --start-file='mysql-bin.000002' --start-pos=763 --end-pos=1147
|
shell> python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttest3 --start-file='mysql-bin.000002' --start-pos=763 --end-pos=1147
|
||||||
|
|
||||||
输出:
|
输出:
|
||||||
INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 981 end 1147
|
INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 981 end 1147
|
||||||
|
@ -106,15 +104,15 @@ mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
||||||
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
||||||
| 3 | 小孙 | 2016-12-10 00:04:51 |
|
| 3 | 小孙 | 2016-12-13 20:25:00 |
|
||||||
| 4 | 小李 | 2016-12-10 00:04:56 |
|
| 4 | 小李 | 2016-12-12 00:00:00 |
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
4 rows in set (0.00 sec)
|
4 rows in set (0.00 sec)
|
||||||
|
|
||||||
mysql> delete from tbl;
|
mysql> delete from tbl;
|
||||||
Query OK, 4 rows affected (0.00 sec)
|
Query OK, 4 rows affected (0.00 sec)
|
||||||
|
|
||||||
tbl表被清空
|
20:28时,tbl表误操作被清空
|
||||||
mysql> select * from tbl;
|
mysql> select * from tbl;
|
||||||
Empty set (0.00 sec)
|
Empty set (0.00 sec)
|
||||||
```
|
```
|
||||||
|
@ -124,41 +122,44 @@ Empty set (0.00 sec)
|
||||||
1. 登录mysql,查看目前的binlog文件
|
1. 登录mysql,查看目前的binlog文件
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mysql> show master logs;
|
mysql> show master status;
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
| Log_name | File_size |
|
| Log_name | File_size |
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
| mysql-bin.000046 | 12262268 |
|
| mysql-bin.000051 | 967 |
|
||||||
| mysql-bin.000047 | 3583 |
|
| mysql-bin.000052 | 965 |
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 最新的binlog文件是mysql-bin.000047,我们再定位误操作SQL的binlog位置
|
2. 最新的binlog文件是mysql-bin.000052,我们再定位误操作SQL的binlog位置。误操作人只能知道大致的误操作时间,我们根据大致时间过滤数据。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047'
|
shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-datetime='2016-12-13 20:25:00' --stop-datetime='2016-12-13 20:30:00'
|
||||||
输出:
|
输出:
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:33' AND `id`=1 AND `name`='小赵' LIMIT 1; #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:25:00', 3, '小孙'); #start 4 end 290 time 2016-12-13 20:25:46
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:48' AND `id`=2 AND `name`='小钱' LIMIT 1; #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:26:00', 4, '小李'); #start 317 end 487 time 2016-12-13 20:26:26
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:51' AND `id`=3 AND `name`='小孙' LIMIT 1; #start 3346 end 3556
|
UPDATE `test`.`tbl` SET `addtime`='2016-12-12 00:00:00', `id`=4, `name`='小李' WHERE `addtime`='2016-12-13 20:26:00' AND `id`=4 AND `name`='小李' LIMIT 1; #start 514 end 701 time 2016-12-13 20:27:07
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:56' AND `id`=4 AND `name`='小李' LIMIT 1; #start 3346 end 3556
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:33' AND `id`=1 AND `name`='小赵' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:48' AND `id`=2 AND `name`='小钱' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-13 20:25:00' AND `id`=3 AND `name`='小孙' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-12 00:00:00' AND `id`=4 AND `name`='小李' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 生成回滚sql,并检查回滚sql是否正确
|
3. 我们得到了误操作sql的准确位置在728-938之间,再根据位置进一步过滤,使用flashback模式生成回滚sql,检查回滚sql是否正确
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047' --start-pos=3346 --end-pos=3556 -B
|
shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-pos=3346 --end-pos=3556 -B
|
||||||
输出:
|
输出:
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:56', 4, '小李'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-12 00:00:00', 4, '小李'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:51', 3, '小孙'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:25:00', 3, '小孙'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:48', 2, '小钱'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:48', 2, '小钱'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:33', 1, '小赵'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:33', 1, '小赵'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 确认回滚sql正确,执行回滚语句。登录mysql确认,数据回滚成功。
|
3. 确认回滚sql正确,执行回滚语句。登录mysql确认,数据回滚成功。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047' --start-pos=3346 --end-pos=3556 -B | mysql -h127.0.0.1 -P3306 -uadmin -p'admin'
|
shell> python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-pos=3346 --end-pos=3556 -B | mysql -h127.0.0.1 -P3306 -uadmin -p'admin'
|
||||||
|
|
||||||
mysql> select * from tbl;
|
mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
|
@ -166,8 +167,8 @@ mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
||||||
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
||||||
| 3 | 小孙 | 2016-12-10 00:04:51 |
|
| 3 | 小孙 | 2016-12-13 20:25:00 |
|
||||||
| 4 | 小李 | 2016-12-10 00:04:56 |
|
| 4 | 小李 | 2016-12-12 00:00:00 |
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import os, datetime
|
||||||
import pymysql
|
import pymysql
|
||||||
from pymysqlreplication import BinLogStreamReader
|
from pymysqlreplication import BinLogStreamReader
|
||||||
from pymysqlreplication.row_event import (
|
from pymysqlreplication.row_event import (
|
||||||
|
@ -14,8 +14,8 @@ from binlog2sql_util import command_line_args, concat_sql_from_binlogevent, crea
|
||||||
|
|
||||||
class Binlog2sql(object):
|
class Binlog2sql(object):
|
||||||
|
|
||||||
def __init__(self, connectionSettings, startFile=None, startPos=None, endFile=None,
|
def __init__(self, connectionSettings, startFile=None, startPos=None, endFile=None, endPos=None, startTime=None,
|
||||||
endPos=None, only_schemas=None, only_tables=None, popPk=False, flashback=False, stopnever=False):
|
stopTime=None, only_schemas=None, only_tables=None, popPk=False, flashback=False, stopnever=False):
|
||||||
'''
|
'''
|
||||||
connectionSettings: {'host': 127.0.0.1, 'port': 3306, 'user': slave, 'passwd': slave}
|
connectionSettings: {'host': 127.0.0.1, 'port': 3306, 'user': slave, 'passwd': slave}
|
||||||
'''
|
'''
|
||||||
|
@ -24,9 +24,11 @@ class Binlog2sql(object):
|
||||||
|
|
||||||
self.connectionSettings = connectionSettings
|
self.connectionSettings = connectionSettings
|
||||||
self.startFile = startFile
|
self.startFile = startFile
|
||||||
self.startPos = startPos if startPos else 4
|
self.startPos = startPos if startPos else 4 # use binlog v4
|
||||||
self.endFile = endFile if endFile else startFile
|
self.endFile = endFile if endFile else startFile
|
||||||
self.endPos = endPos
|
self.endPos = endPos
|
||||||
|
self.startTime = datetime.datetime.strptime(startTime, "%Y-%m-%d %H:%M:%S") if startTime else datetime.datetime.strptime('1970-01-01 00:00:00', "%Y-%m-%d %H:%M:%S")
|
||||||
|
self.stopTime = datetime.datetime.strptime(stopTime, "%Y-%m-%d %H:%M:%S") if stopTime else datetime.datetime.strptime('2999-12-31 00:00:00', "%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
self.only_schemas = only_schemas if only_schemas else None
|
self.only_schemas = only_schemas if only_schemas else None
|
||||||
self.only_tables = only_tables if only_tables else None
|
self.only_tables = only_tables if only_tables else None
|
||||||
|
@ -69,9 +71,9 @@ class Binlog2sql(object):
|
||||||
if not self.stopnever:
|
if not self.stopnever:
|
||||||
if (stream.log_file == self.endFile and stream.log_pos == self.endPos) or (stream.log_file == self.eofFile and stream.log_pos == self.eofPos):
|
if (stream.log_file == self.endFile and stream.log_pos == self.endPos) or (stream.log_file == self.eofFile and stream.log_pos == self.eofPos):
|
||||||
flagLastEvent = True
|
flagLastEvent = True
|
||||||
elif stream.log_file not in self.binlogList:
|
elif datetime.datetime.fromtimestamp(binlogevent.__dict__['timestamp']) < self.startTime:
|
||||||
break
|
continue
|
||||||
elif (self.endPos and stream.log_file == self.endFile and stream.log_pos > self.endPos) or (stream.log_file == self.eofFile and stream.log_pos > self.eofPos):
|
elif (stream.log_file not in self.binlogList) or (self.endPos and stream.log_file == self.endFile and stream.log_pos > self.endPos) or (stream.log_file == self.eofFile and stream.log_pos > self.eofPos) or (datetime.datetime.fromtimestamp(binlogevent.__dict__['timestamp']) >= self.stopTime):
|
||||||
break
|
break
|
||||||
# else:
|
# else:
|
||||||
# raise ValueError('unknown binlog file or position')
|
# raise ValueError('unknown binlog file or position')
|
||||||
|
@ -117,5 +119,6 @@ if __name__ == '__main__':
|
||||||
connectionSettings = {'host':args.host, 'port':args.port, 'user':args.user, 'passwd':args.password}
|
connectionSettings = {'host':args.host, 'port':args.port, 'user':args.user, 'passwd':args.password}
|
||||||
binlog2sql = Binlog2sql(connectionSettings=connectionSettings, startFile=args.startFile,
|
binlog2sql = Binlog2sql(connectionSettings=connectionSettings, startFile=args.startFile,
|
||||||
startPos=args.startPos, endFile=args.endFile, endPos=args.endPos,
|
startPos=args.startPos, endFile=args.endFile, endPos=args.endPos,
|
||||||
only_schemas=args.databases, only_tables=args.tables, popPk=args.popPk, flashback=args.flashback, stopnever=args.stopnever)
|
startTime=args.startTime, stopTime=args.stopTime, only_schemas=args.databases,
|
||||||
|
only_tables=args.tables, popPk=args.popPk, flashback=args.flashback, stopnever=args.stopnever)
|
||||||
binlog2sql.process_binlog()
|
binlog2sql.process_binlog()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os, sys, argparse
|
import os, sys, argparse, datetime
|
||||||
import pymysql
|
import pymysql
|
||||||
from pymysqlreplication import BinLogStreamReader
|
from pymysqlreplication import BinLogStreamReader
|
||||||
from pymysqlreplication.row_event import (
|
from pymysqlreplication.row_event import (
|
||||||
|
@ -11,6 +11,14 @@ from pymysqlreplication.row_event import (
|
||||||
)
|
)
|
||||||
from pymysqlreplication.event import QueryEvent, RotateEvent, FormatDescriptionEvent
|
from pymysqlreplication.event import QueryEvent, RotateEvent, FormatDescriptionEvent
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_datetime(string):
|
||||||
|
try:
|
||||||
|
datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
def create_unique_file(filename):
|
def create_unique_file(filename):
|
||||||
version = 0
|
version = 0
|
||||||
resultFile = filename
|
resultFile = filename
|
||||||
|
@ -40,11 +48,15 @@ def command_line_parser():
|
||||||
range.add_argument('--start-file', dest='startFile', type=str,
|
range.add_argument('--start-file', dest='startFile', type=str,
|
||||||
help='Start binlog file to be parsed')
|
help='Start binlog file to be parsed')
|
||||||
range.add_argument('--start-pos', dest='startPos', type=int,
|
range.add_argument('--start-pos', dest='startPos', type=int,
|
||||||
help='start position of start binlog file', default=4)
|
help='start position of the --start-file', default=4)
|
||||||
range.add_argument('--end-file', dest='endFile', type=str,
|
range.add_argument('--end-file', dest='endFile', type=str,
|
||||||
help="End binlog file to be parsed. default: '--start-file'", default='')
|
help="End binlog file to be parsed. default: '--start-file'", default='')
|
||||||
range.add_argument('--end-pos', dest='endPos', type=int,
|
range.add_argument('--end-pos', dest='endPos', type=int,
|
||||||
help="stop position of end binlog file. default: end position of '--end-file'", default=0)
|
help="stop position of --end-file. default: end position of '--end-file'", default=0)
|
||||||
|
range.add_argument('--start-datetime', dest='startTime', type=str,
|
||||||
|
help="Start reading the binlog at first event having a datetime equal or posterior to the argument; the argument must be a date and time in the local time zone, in any format accepted by the MySQL server for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 (you should probably use quotes for your shell to set it properly).", default='')
|
||||||
|
range.add_argument('--stop-datetime', dest='stopTime', type=str,
|
||||||
|
help="Stop reading the binlog at first event having a datetime equal or posterior to the argument; the argument must be a date and time in the local time zone, in any format accepted by the MySQL server for DATETIME and TIMESTAMP types, for example: 2004-12-25 11:25:56 (you should probably use quotes for your shell to set it properly).", default='')
|
||||||
parser.add_argument('--stop-never', dest='stopnever', action='store_true',
|
parser.add_argument('--stop-never', dest='stopnever', action='store_true',
|
||||||
help='Wait for more data from the server. default: stop replicate at the last binlog when you start binlog2sql', default=False)
|
help='Wait for more data from the server. default: stop replicate at the last binlog when you start binlog2sql', default=False)
|
||||||
|
|
||||||
|
@ -73,6 +85,8 @@ def command_line_args():
|
||||||
raise ValueError('only one of flashback or stop-never can be True')
|
raise ValueError('only one of flashback or stop-never can be True')
|
||||||
if args.flashback and args.popPk:
|
if args.flashback and args.popPk:
|
||||||
raise ValueError('only one of flashback or popPk can be True')
|
raise ValueError('only one of flashback or popPk can be True')
|
||||||
|
if (args.startTime and not is_valid_datetime(args.startTime)) or (args.stopTime and not is_valid_datetime(args.stopTime)):
|
||||||
|
raise ValueError('Incorrect date and time argument')
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,5 +161,5 @@ def concat_sql_from_binlogevent(cursor, binlogevent, row=None, eStartPos=None, f
|
||||||
binlogevent.schema, fix_object(binlogevent.query)
|
binlogevent.schema, fix_object(binlogevent.query)
|
||||||
)
|
)
|
||||||
if type(binlogevent) in (WriteRowsEvent, UpdateRowsEvent, DeleteRowsEvent):
|
if type(binlogevent) in (WriteRowsEvent, UpdateRowsEvent, DeleteRowsEvent):
|
||||||
sql += ' #start %s end %s' % (eStartPos, binlogevent.packet.log_pos)
|
sql += ' #start %s end %s time %s' % (eStartPos, binlogevent.packet.log_pos, datetime.datetime.fromtimestamp(binlogevent.__dict__['timestamp']))
|
||||||
return sql
|
return sql
|
||||||
|
|
|
@ -28,7 +28,7 @@ pip install -r requirements.txt
|
||||||
|
|
||||||
然后,我们就可以生成回滚SQL了。
|
然后,我们就可以生成回滚SQL了。
|
||||||
|
|
||||||
**背景**:误删了test库tbl表整张表的数据,需要紧急回滚。
|
**背景**:小明在20点多时误删了test库tbl表整张表的数据,需要紧急回滚。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
test库tbl表原有数据
|
test库tbl表原有数据
|
||||||
|
@ -38,15 +38,15 @@ mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
||||||
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
||||||
| 3 | 小孙 | 2016-12-10 00:04:51 |
|
| 3 | 小孙 | 2016-12-13 20:25:00 |
|
||||||
| 4 | 小李 | 2016-12-10 00:04:56 |
|
| 4 | 小李 | 2016-12-12 00:00:00 |
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
4 rows in set (0.00 sec)
|
4 rows in set (0.00 sec)
|
||||||
|
|
||||||
mysql> delete from tbl;
|
mysql> delete from tbl;
|
||||||
Query OK, 4 rows affected (0.00 sec)
|
Query OK, 4 rows affected (0.00 sec)
|
||||||
|
|
||||||
tbl表被清空
|
20:28时,tbl表误操作被清空
|
||||||
mysql> select * from tbl;
|
mysql> select * from tbl;
|
||||||
Empty set (0.00 sec)
|
Empty set (0.00 sec)
|
||||||
```
|
```
|
||||||
|
@ -56,41 +56,44 @@ Empty set (0.00 sec)
|
||||||
1. 登录mysql,查看目前的binlog文件
|
1. 登录mysql,查看目前的binlog文件
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mysql> show master logs;
|
mysql> show master status;
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
| Log_name | File_size |
|
| Log_name | File_size |
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
| mysql-bin.000046 | 12262268 |
|
| mysql-bin.000051 | 967 |
|
||||||
| mysql-bin.000047 | 3583 |
|
| mysql-bin.000052 | 965 |
|
||||||
+------------------+-----------+
|
+------------------+-----------+
|
||||||
```
|
```
|
||||||
|
|
||||||
2. 最新的binlog文件是mysql-bin.000047,我们再定位误操作SQL的binlog位置
|
2. 最新的binlog文件是mysql-bin.000052,我们再定位误操作SQL的binlog位置。误操作人只能知道大致的误操作时间,我们根据大致时间过滤数据。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047'
|
shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-datetime='2016-12-13 20:25:00' --stop-datetime='2016-12-13 20:30:00'
|
||||||
输出:
|
输出:
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:33' AND `id`=1 AND `name`='小赵' LIMIT 1; #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:25:00', 3, '小孙'); #start 4 end 290 time 2016-12-13 20:25:46
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:48' AND `id`=2 AND `name`='小钱' LIMIT 1; #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:26:00', 4, '小李'); #start 317 end 487 time 2016-12-13 20:26:26
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:51' AND `id`=3 AND `name`='小孙' LIMIT 1; #start 3346 end 3556
|
UPDATE `test`.`tbl` SET `addtime`='2016-12-12 00:00:00', `id`=4, `name`='小李' WHERE `addtime`='2016-12-13 20:26:00' AND `id`=4 AND `name`='小李' LIMIT 1; #start 514 end 701 time 2016-12-13 20:27:07
|
||||||
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:56' AND `id`=4 AND `name`='小李' LIMIT 1; #start 3346 end 3556
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:33' AND `id`=1 AND `name`='小赵' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-10 00:04:48' AND `id`=2 AND `name`='小钱' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-13 20:25:00' AND `id`=3 AND `name`='小孙' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
|
DELETE FROM `test`.`tbl` WHERE `addtime`='2016-12-12 00:00:00' AND `id`=4 AND `name`='小李' LIMIT 1; #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 生成回滚sql,并检查回滚sql是否正确
|
3. 我们得到了误操作sql的准确位置在728-938之间,再根据位置进一步过滤,使用flashback模式生成回滚sql,检查回滚sql是否正确
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047' --start-pos=3346 --end-pos=3556 -B
|
shell> python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-pos=3346 --end-pos=3556 -B
|
||||||
输出:
|
输出:
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:56', 4, '小李'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-12 00:00:00', 4, '小李'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:51', 3, '小孙'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-13 20:25:00', 3, '小孙'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:48', 2, '小钱'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:48', 2, '小钱'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:33', 1, '小赵'); #start 3346 end 3556
|
INSERT INTO `test`.`tbl`(`addtime`, `id`, `name`) VALUES ('2016-12-10 00:04:33', 1, '小赵'); #start 728 end 938 time 2016-12-13 20:28:05
|
||||||
```
|
```
|
||||||
|
|
||||||
4. 确认回滚sql正确,执行回滚语句。登录mysql确认,数据回滚成功。
|
3. 确认回滚sql正确,执行回滚语句。登录mysql确认,数据回滚成功。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000047' --start-pos=3346 --end-pos=3556 -B | mysql -h127.0.0.1 -P3306 -uadmin -p'admin'
|
shell> python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttbl --start-file='mysql-bin.000052' --start-pos=3346 --end-pos=3556 -B | mysql -h127.0.0.1 -P3306 -uadmin -p'admin'
|
||||||
|
|
||||||
mysql> select * from tbl;
|
mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
|
@ -98,8 +101,8 @@ mysql> select * from tbl;
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
| 1 | 小赵 | 2016-12-10 00:04:33 |
|
||||||
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
| 2 | 小钱 | 2016-12-10 00:04:48 |
|
||||||
| 3 | 小孙 | 2016-12-10 00:04:51 |
|
| 3 | 小孙 | 2016-12-13 20:25:00 |
|
||||||
| 4 | 小李 | 2016-12-10 00:04:56 |
|
| 4 | 小李 | 2016-12-12 00:00:00 |
|
||||||
+----+--------+---------------------+
|
+----+--------+---------------------+
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue