【必赢娱乐】MySQL利用binlog恢复误操作数据,bi

作者:计算机知识

前言

  在近些日子的工作中,由于本身粗(zuo)心(si)误update操作形成几百行的数目出现错误,在焦灼的还要(那时候我居然不知底除了备份之后还有binlog日志苏醒)立马查资料学习binlog的东山复起,随后马上展开了过来。就算能够假装本人没出错(emmmmm......最终还是得料定的!),但下班之后激情不能够持久平复,立马展开计算机进行3次试行记录本领对得起本身犯的不当。

  注:本次试验是在Wnidows下开始展览的(英特网Linux挺多,不过Windows的吗少,加上笔者小编的微型Computer也是Win七就轻便做一回试验吧!

 


 

binlog二sql落实MySQL误操作的重作冯妇,binlog二sqlmysql

 

对此MySQL数据库中的误操作删除数据的上涨难点,能够运用基于MySQL中binlog做到类似于闪回只怕变化反向操作的SQL语句来贯彻,是MySQL中2个不行实用的法力。
规律简单精通,基于MySQL的row格式的binlog中,记录历史的增加和删除改SQL新闻,基于此深入分析出来对应的SQL语句(回滚的话正是反向的SQL语句)。
在格式为binlog格式为row的日记形式下,binlog中的内容记录了数据库中曾经实施的增加和删除改新闻,都是含有了反向新闻的
必赢娱乐,举例推行delete from table where pk_id = 一;遵照主键来删除一条记下
【必赢娱乐】MySQL利用binlog恢复误操作数据,binlog2sql实现MySQL误操作的恢复。对应的binlog中的sql语句为:delete from table where pk_id = 1 and name = 'xxx' and other_column = 'xxx';where条件不仅是原始语句的Id,而且还包蕴中那1行全体的字段的消息的
update操作也同理,不但记录了update操作,同时记录了update记录在立异此前的每1个字段的值。那样就可以利用那个日志来变化反向操作音信。

 

如下是选拔mysqlbinlog 工具剖析出来的一个MySQL中独立的binlog日志文件的部分内容,能够精通地收看进行过的sql语句的音信。
谈到那边,对于MySQL中基于binlog的一部分用到,比方复制或然数据库还原,其实正是双重试行某些数据库上的野史实行过的增删改SQL语句来达成的。
题外话:MySQL的binlog功能记录事务语句的效益上,基本上等同于SQLServer的的事情日志。
不过SQL Server的专门的职业日志正的2进制内容的,微软官方也并未有提供分析的艺术,而MySQL中完全能够透过mysqlbinlog 来深入分析出来那一个日志中的内容。

如下是透过MySQL自带的mysqlbinlog工具剖判出来的binlog日志文件中的消息,能够见到里面包车型客车SQL语句音讯。

必赢娱乐 1

 

掌握了binlog中的内容,就能够依附这些binlog来兑现各样实用的意义,规范的正是误删数据的余烬复起操作,譬喻苏家小萝卜同学就自个儿用Python化解那几个剖析功效
就如意义相比较盛名的还有大众点评网DBA自身写的binlog贰sql工具,也是久闻大名,终于有机会品尝了。
binlog二sql内需语句pip安装,所以需求先安装pip
pip 安装参照他事他说加以侦查:

必赢娱乐 2

必赢娱乐 3

binlog二sql下载以及安装:

必赢娱乐 4

成就了binlog二sql自此,就足以选拔它来达成数据的恢复生机操作了,如下模拟几个误操作的还原

在开启了binlog,日志格式为row的测试数据下,对于测试表test_01,分别实施以下sql语句:

insert into test_01 values (1,'aaa');
insert into test_01 values (2,'bbb');
insert into test_01 values (3,'vvv');
--以下误操作,更新了全部数据
update test_01 set name = 'xxx';

透过show master logs;找到当前的binlog文件,对应的sql语句的执行就存款和储蓄在当下以此binlog中,binlog二sql的靶子就是以此文件

必赢娱乐 5

参照下图,能够窥见
执行:python binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'root' -ddb01 -t test_01 --start-file='binlog.000021' (更加多参数以及接纳方式参照他事他说加以调查下文链接),通过binlog2sql来剖判当前的binlog文件,
浅析出来的SQL语句便是正规SQL语句的执行(insert insert insert update(叁行笔录))
执行:python binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'root' -ddb01 -t test_01 --start-file='binlog.0000贰一' -B,通过-B参数生成反向的操作信息
加参数-B分析出来的SQL语句与地点的SQL语句刚好相反,包涵各类,也即以倒序的主意转换反向的操作
原有操作是insert insert insert update update update,反向的操作就是upfate update update delete delete delete,
那样壹来,能够依据现实的情状,截取生成的反向的sql语句,实行误操作的恢复生机。

以上操作注意安装的binlog贰sql的门路难题,假使路线不对,找不到binlog二sql.py,上述命令也就不能实行

必赢娱乐 6

更加多binlog2sql参数以及用法和限制参照他事他说加以考查官方GitHub:

 

对于MySQL数据库中的误操作删除数据的上涨难题,能够运用基于MySQL中binlog做到类似于闪回也许...

 

在人工手动进行部分数据库写操作的时候(举例说数据更正),尤其是有些不可控的批量翻新或删除,日常都建议备份后操作。然而不怕万一,就怕30000,桑土准备总是好的。在线上也许测试意况误操作导致数据被去除或许更新后,想要复苏,一般有二种方式。
措施壹、利用近些日子的全量备份 增量binlog备份,恢复生机到误操作此前的情事,不过随着数据量的增大,binlog的加码,恢复生机起来很讨厌。
措施2、倘使binlog的格式为row,那么就能够将binlog解析出来生成反向的原始SQL

1、初识binlog

(一)MySQL的binlog正是豪门经常所说的Binary Log,即bin-log,是MySQL存款和储蓄对数据库改造的二进制文件,也正是记录了有着DDL与DML(select除了这几个之外)语句,利用它根本能够做两件事:

  • 数据苏醒:通过mysqlbinog工具举行理并答复苏;
  • 多少复制:MySQL Replication在Master端开启binlog,Mster把它的二进制日志传递给slaves来到达master-slave数据壹致的指标。

(2)怎么查看MySQL的日记境况(是或不是开启等),当MySQL还没张开时候,通过命令:show variables like ‘log_bin%'查看

mysql> show variables like 'log_bin%';
 --------------------------------- ------- 
| Variable_name                   | Value |
 --------------------------------- ------- 
| log_bin                         | OFF   |
| log_bin_basename                |       |
| log_bin_index                   |       |
| log_bin_trust_function_creators | OFF   |
| log_bin_use_v1_row_events       | OFF   |
 --------------------------------- ------- 
5 rows in set

(3)如何在Wnidows在修改log_bin状态为ON开启呢?

  • 找到C:ProgramDataMySQLMySQL Server 5.7my.ini文件(专注:是C盘下隐藏文件夹ProgramData,而不是Program Files下)  

  必赢娱乐 7

  • 充实/修改常用的陈设属性

  必赢娱乐 8

(四)常用简单属性表达:除了上述log_bin、binlog_format多个大概的安排外,仍可以有其余的天性配置

  • log-bin = /xxx/xxx/mysql_bin #binlog日志文件,以mysql_bin开头,六个数字结尾的文件:mysql_bin.000001,并且会将文件存储在相应的xxx/xxx路径下,如果只配置mysql_bin的话默认在C:ProgramDataMySQLMySQL Server 5.7Data下;``
  • binlog_format = ROW #binlog日志格式,默认为STATEMENT:每一条SQL语句都会被记录;ROW:仅记录哪条数据被修改并且修改成什么样子,是binlog开启并且能恢复数据的关键;
  • expire_logs_days= 7 #binlog过期清理时间;
  • ``max_binlog_size = 100m #binlog每个日志文件大小;
  • binlog_cache_size = 4m #binlog缓存大小;
  • max_binlog_cache_size = 512m #最大binlog缓存大小。

对此MySQL数据库中的误操作删除数据的回涨难点,可以行使基于MySQL中binlog做到类似于闪回可能变化反向操作的SQL语句来落实,是MySQL中二个卓殊实用的功力。
原理简单明白,基于MySQL的row格式的binlog中,记录历史的增加和删除改SQL音信,基于此深入分析出来对应的SQL语句(回滚的话就是反向的SQL语句)。
在格式为binlog格式为row的日记格局下,binlog中的内容记录了数据库中曾经实行的增删改消息,都以富含了反向新闻的
诸如推行delete from table where pk_id = 一;依据主键来删除一条记下
对应的binlog中的sql语句为:delete from table where pk_id = 1 and name = 'xxx' and other_column = 'xxx';where条件不止是原始语句的Id,而且还包括中那一行全数的字段的音讯的
update操作也同理,不但记录了update操作,同时记录了update记录在创新在此之前的每2个字段的值。那样就能够利用那个日志来扭转反向操作消息。

以下是使用情势贰写的多个python脚本binlog_rollback.py,可利用此脚本生成反向的原始SQL。

二、苏醒数据测试

(1)准备表user

mysql> select * from user;
 ---- ---------- ---------------------------------- 
| id | name     | password                         |
 ---- ---------- ---------------------------------- 
|  1 | Zhangsan | 2d7284808e5111e8af74201a060059ce |
|  2 | Lisi     | 2d73641c8e5111e8af74201a060059ce |
|  3 | Wangwu   | 2d73670c8e5111e8af74201a060059ce |
 ---- ---------- ---------------------------------- 
3 rows in set

 (2)误update苏醒,比方自个儿在update user set name = 'Lijian' where id = 1;的时候忘写where id = 一首要尺度,结果产生整个数量被更新

mysql> update user set name ='Lijian';
Query OK, 3 rows affected
Rows matched: 3  Changed: 3  Warnings: 0
mysql> select*from user;
 ---- -------- ---------------------------------- 
| id | name   | password                         |
 ---- -------- ---------------------------------- 
|  1 | Lijian | 2d7284808e5111e8af74201a060059ce |
|  2 | Lijian | 2d73641c8e5111e8af74201a060059ce |
|  3 | Lijian | 2d73670c8e5111e8af74201a060059ce |
 ---- -------- ---------------------------------- 
3 rows in set

  那个时候你势必很慌,然而先不要慌(实际上慌也没用),先看未有备份,如若未有再看是或不是开启binlog(show variables like ‘log_bin%'),借使两岸都未有(作者深信大家都会定时备份 binlog)从数据库那几个规模是无能为力恢复的了,要是binlog开启的话,1切都好说。就起来执行上面几步恢复生机吧!

  率先步:找到当前mysql记录的binlog文件,推行show master status;

  第贰步:查看binlog,定位误操作的pos只怕时间段。实施show binlog events in 'mysql_bin.000001';

mysql> show binlog events in 'mysql_bin.000001';
 ------------------ ----- ---------------- ----------- ------------- --------------------------------------- 
| Log_name         | Pos | Event_type     | Server_id | End_log_pos | Info                                  |
 ------------------ ----- ---------------- ----------- ------------- --------------------------------------- 
| mysql_bin.000001 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.12-log, Binlog ver: 4 |
| mysql_bin.000001 | 123 | Previous_gtids |         1 |         154 |                                       |
| mysql_bin.000001 | 154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'  |
| mysql_bin.000001 | 219 | Query          |         1 |         291 | BEGIN                                 |
| mysql_bin.000001 | 291 | Table_map      |         1 |         344 | table_id: 108 (test.user)             |
| mysql_bin.000001 | 344 | Update_rows    |         1 |         650 | table_id: 108 flags: STMT_END_F       |
| mysql_bin.000001 | 650 | Xid            |         1 |         681 | COMMIT /* xid=22 */                   |
 ------------------ ----- ---------------- ----------- ------------- --------------------------------------- 
7 rows in set

  第三步:进入C:ProgramDataMySQLMySQL Server 5.7Data执行mysqlbinlog --start-position=219 --stop-position=681 mysql-bin.000001 > e:\update.sql将update部分单独备份出来到E盘下为update.sql  

 必赢娱乐 9 

  第四步:登录mysql(mysql -uroot -p123)

  第伍步:试行source e:update.sql复苏数据,部分截图如下:

  必赢娱乐 10

  **第四步:查看结果**

mysql> select * from user;
 ---- ---------- ---------------------------------- 
| id | name     | password                         |
 ---- ---------- ---------------------------------- 
|  1 | Zhangsan | 2d7284808e5111e8af74201a060059ce |
|  2 | Lisi     | 2d73641c8e5111e8af74201a060059ce |
|  3 | Wangwu   | 2d73670c8e5111e8af74201a060059ce |
 ---- ---------- ---------------------------------- 
3 rows in set

 

说明:

0、前提是binlog的格式为row
一、要还原的表操作前后表结构未有发生更换,不然脚本非常的小概分析
2、只生成DML(insert/update/delete)的rollback语句
三、最后生成的SQL是逆序的,所以新型的DML会变动在输入文件的最前头,并且带上了时光戳和偏移点,方便找出指标
四、需求提供七个连连MySQL的只读用户,首假如为着赢得表结构
五、倘使binlog过大,提议带上时间限定,也得以内定只回复有个别库的SQL
陆、SQL生成后,请务必在测试蒙受上测试恢复生机后再接纳到线上

3、总结

  (1)行使binlog只好针对针对数据量不是许多的情景,真正的生产条件每种多少个G的日记文件,不唯有是光靠binlog苏醒的,还有越来越多的法门,在此只是做叁个轻便的学习记录!

  (二)决断时间binlog日志的岁月阶段与pos地点很关键,可是要求精通有关binlog的多多参数!

  (叁)在此之前几天真的以为开采人士就无需太通晓数据库相关的运行,然而明天经历过才清楚数据库的有关文化也是开辟人士必须掌握的!

 

一般来讲是利用mysqlbinlog 工具剖析出来的2个MySQL中超级的binlog日志文件的一对内容,能够清楚地看来实行过的sql语句的音讯。
聊起此处,对于MySQL中基于binlog的片段行使,比方复制大概数据库还原,其实正是再一次试行有个别数据库上的野史实行过的增加和删除改SQL语句来兑现的。
题外话:MySQL的binlog功用记录事务语句的功用上,基本上等同于SQLServer的的事务日志。
而是SQL Server的思想政治工作日志正的二进制内容的,微软官方也并未有提供深入分析的不二等秘书诀,而MySQL中完全能够通过mysqlbinlog 来剖析出来这些日志中的内容。

演示

#首先创建一个只读账号
root:test> grant select on *.* to 'query'@'127.0.0.1' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.01 sec)

#测试表结构如下
root:test> CREATE TABLE `table1` (
    ->   `id` int(11) NOT NULL AUTO_INCREMENT,
    ->   `c1` int(11) DEFAULT NULL,
    ->   `c2` varchar(20) DEFAULT NULL,
    ->   `c3` int(11) DEFAULT NULL,
    ->   PRIMARY KEY (`id`)
    -> );
Query OK, 0 rows affected (0.09 sec)

#插入三条数据
root:test> insert into table1(c1,c2,c3) values (1,'a',1),(2,'b',2),(3,'c',3);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

root:test> select * from table1;
 ---- ------ ------ ------ 
| id | c1   | c2   | c3   |
 ---- ------ ------ ------ 
|  1 |    1 | a    |    1 |
|  2 |    2 | b    |    2 |
|  3 |    3 | c    |    3 |
 ---- ------ ------ ------ 
3 rows in set (0.00 sec)

#更新一条数据
root:test> update table1 set c3=10 where id=3;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

root:test> select * from table1;
 ---- ------ ------ ------ 
| id | c1   | c2   | c3   |
 ---- ------ ------ ------ 
|  1 |    1 | a    |    1 |
|  2 |    2 | b    |    2 |
|  3 |    3 | c    |   10 |
 ---- ------ ------ ------ 
3 rows in set (0.00 sec)

#删除一条数据

root:test> delete from table1 where id=1;
Query OK, 1 row affected (0.01 sec)

root:test> select * from table1;
 ---- ------ ------ ------ 
| id | c1   | c2   | c3   |
 ---- ------ ------ ------ 
|  2 |    2 | b    |    2 |
|  3 |    3 | c    |   10 |
 ---- ------ ------ ------ 
2 rows in set (0.00 sec)

接下去利用脚本来生成反向SQL

[root@diandi ~]# python binlog_rollback.py -f /log/mysql/bin/mysql-bin.000002  -o rollback.sql -u query -p 123456 --start-datetime='2016-10-28 00:00:00' -d test
正在获取参数.....
正在解析binlog.....
正在初始化列名.....
正在开始拼凑sql.....
done!

#查看反向SQL,最新的DML会生成在输入文件的最前面
[root@diandi ~]# cat rollback.sql 
## at 155848
##161028 17:07:10 server id 22100  end_log_pos 155898 CRC32 0x5000bca7  Delete_rows: table id 351 flags: STMT_END_F
INSERT INTO `test`.`table1`
SET
  id=1
  ,c1=1
  ,c2='a'
  ,c3=1;
## at 155560
##161028 17:04:56 server id 22100  end_log_pos 155626 CRC32 0x11d91e2d  Update_rows: table id 351 flags: STMT_END_F
UPDATE `test`.`table1`
SET
  id=3
  ,c1=3
  ,c2='c'
  ,c3=3
WHERE
  id=3
  AND c1=3
  AND c2='c'
  AND c3=10;
## at 155258
##161028 16:59:31 server id 22100  end_log_pos 155338 CRC32 0x3978c1c1  Write_rows: table id 351 flags: STMT_END_F
DELETE FROM `test`.`table1`
WHERE
  id=3
  AND c1=3
  AND c2='c'
  AND c3=3;
DELETE FROM `test`.`table1`
WHERE
  id=2
  AND c1=2
  AND c2='b'
  AND c3=2;
DELETE FROM `test`.`table1`
WHERE
  id=1
  AND c1=1
  AND c2='a'
  AND c3=1;

进行回滚操作

#直接source整个文件,table1将恢复到原来的空表状态(实际情况,在测试环境上按需索取,然后再恢复线上)
root:test> source /root/rollback.sql
Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.01 sec)

Query OK, 1 row affected (0.01 sec)

root:test> select * from table1;
Empty set (0.00 sec)

实际的参数使用方法如下:

[root@diandi ~]# python binlog_rollback.py 
==========================================================================================
Command line options :
    --help                  # OUT : print help info
    -f, --binlog            # IN  : binlog file. (required)
    -o, --outfile           # OUT : output rollback sql file. (default 'rollback.sql')
    -h, --host              # IN  : host. (default '127.0.0.1')
    -u, --user              # IN  : user. (required)
    -p, --password          # IN  : password. (required)
    -P, --port              # IN  : port. (default 3306)
    --start-datetime        # IN  : start datetime. (default '1970-01-01 00:00:00')
    --stop-datetime         # IN  : stop datetime. default '2070-01-01 00:00:00'
    --start-position        # IN  : start position. (default '4')
    --stop-position         # IN  : stop position. (default '18446744073709551615')
    -d, --database          # IN  : List entries for just this database (No default value).
    --only-primary          # IN  : Only list primary key in where condition (default 0)

Sample :
   shell> python binlog_rollback.py -f 'mysql-bin.000001' -o '/tmp/rollback.sql' -h 192.168.0.1 -u 'user' -p 'pwd' -P 3307 -d dbname

如下是通过MySQL自带的mysqlbinlog工具深入分析出来的binlog日志文件中的音讯,能够观察里面包车型大巴SQL语句音信。

剧本代码

#!/bin/env python
# -*- coding:utf-8 -*-

import os,sys,re,getopt
import MySQLdb


host = '127.0.0.1'
user = ''
password = ''
port = 3306
start_datetime = '1971-01-01 00:00:00'
stop_datetime = '2037-01-01 00:00:00'
start_position = '4'
stop_position = '18446744073709551615'
database = ''
mysqlbinlog_bin = 'mysqlbinlog -v'
binlog = ''
fileContent = ''
output='rollback.sql'
only_primary = 0


# ----------------------------------------------------------------------------------------
# 功能:获取参数,生成相应的binlog解析文件
# ----------------------------------------------------------------------------------------
def getopts_parse_binlog():
    global host
    global user
    global password
    global port
    global fileContent
    global output
    global binlog
    global start_datetime
    global stop_datetime
    global start_position
    global stop_position
    global database
    global only_primary
    try:
        options, args = getopt.getopt(sys.argv[1:], "f:o:h:u:p:P:d:", ["help","binlog=","output=","host=","user=","password=","port=","start-datetime=", 
                                                                      "stop-datetime=","start-position=","stop-position=","database=","only-primary="])
    except getopt.GetoptError:
        print "参数输入有误!!!!!"
        options = []
    if options == [] or options[0][0] in ("--help"):
        usage()
        sys.exit()
    print "正在获取参数....."
    for name, value in options:
        if name == "-f" or name == "--binlog":
            binlog = value
        if name == "-o" or name == "--output":
            output = value
        if name == "-h" or name == "--host":
            host = value
        if name == "-u" or name == "--user":
            user = value
        if name == "-p" or name == "--password":
            password = value
        if name == "-P" or name == "--port":
            port = value
        if name == "--start-datetime":
            start_datetime = value
        if name == "--stop-datetime":
            stop_datetime = value
        if name == "--start-position":
            start_position = value
        if name == "--stop-position":
            stop_position = value
        if name == "-d" or name == "--database":
            database = value
        if name == "--only-primary" :
            only_primary = value

    if binlog == '' :
        print "错误:请指定binlog文件名!"
        usage()
    if user == '' :
        print "错误:请指定用户名!"
        usage()
    if password == '' :
        print "错误:请指定密码!"
        usage()
    if database <> '' :
       condition_database = "--database="   "'"   database   "'"
    else:
        condition_database = ''
    print "正在解析binlog....."
    fileContent=os.popen("%s %s  --base64-output=DECODE-ROWS --start-datetime='%s' --stop-datetime='%s' --start-position='%s' --stop-position='%s' %s
                   |grep '###' -B 2|sed -e 's/### //g' -e 's/^INSERT/##INSERT/g' -e 's/^UPDATE/##UPDATE/g' -e 's/^DELETE/##DELETE/g' " 
                   %(mysqlbinlog_bin,binlog,start_datetime,stop_datetime,start_position,stop_position,condition_database)).read()
    #print fileContent



# ----------------------------------------------------------------------------------------
# 功能:初始化binlog里的所有表名和列名,用全局字典result_dict来储存每个表有哪些列
# ----------------------------------------------------------------------------------------
def init_col_name():
    global result_dict
    global pri_dict
    global fileContent
    result_dict = {}
    pri_dict = {}
    table_list = re.findall('`.*`\.`.*`',fileContent)
    table_list = list(set(table_list))
    #table_list 为所有在这段binlog里出现过的表
    print "正在初始化列名....."
    for table in table_list:
        sname = table.split('.')[0].replace('`','')
        tname = table.split('.')[1].replace('`','')
        #连接数据库获取列和列id
        try:
            conn = MySQLdb.connect(host=host,user=user,passwd=password,port=int(port))
            cursor = conn.cursor()
            cursor.execute("select ordinal_position,column_name 
                                                       from information_schema.columns 
                                                       where table_schema='%s' and table_name='%s' " %(sname,tname))

            result=cursor.fetchall()
            if result == () :
                print 'Warning:' sname '.' tname '已删除'
                #sys.exit()
            result_dict[sname '.' tname]=result
            cursor.execute("select ordinal_position,column_name   
                               from information_schema.columns 
                               where table_schema='%s' and table_name='%s' and column_key='PRI' " %(sname,tname))
            pri=cursor.fetchall()
            #print pri
            pri_dict[sname '.' tname]=pri
            cursor.close()
            conn.close()
        except MySQLdb.Error, e:
            try:
                print "Error %d:%s" % (e.args[0], e.args[1])
            except IndexError:
                print "MySQL Error:%s" % str(e)

            sys.exit()
    #print result_dict
    #print pri_dict

# ----------------------------------------------------------------------------------------
# 功能:拼凑回滚sql,逆序
# ----------------------------------------------------------------------------------------
def gen_rollback_sql():
    global only_primary
    fileOutput = open(output, 'w')
    #先将文件根据'--'分块,每块代表一个sql
    area_list=fileContent.split('--n')
    #逆序读取分块
    print "正在开始拼凑sql....."
    for area in area_list[::-1]:
        #由于一条sql可能影响多行,每个sql又可以分成多个逐条执行的sql
        sql_list = area.split('##')
        #先将pos点和timestamp传入输出文件中
        for sql_head in sql_list[0].splitlines():
            sql_head = '#' sql_head 'n'
            fileOutput.write(sql_head)
        #逐条sql进行替换更新,逆序
        for sql in sql_list[::-1][0:-1]:
            try:
                if sql.split()[0] == 'INSERT':
                    rollback_sql = re.sub('^INSERT INTO', 'DELETE FROM', sql, 1)
                    rollback_sql = re.sub('SETn', 'WHEREn', rollback_sql, 1)
                    tablename_pos = 2
                    table_name = rollback_sql.split()[tablename_pos].replace('`', '')
                    # 获取该sql中的所有列
                    col_list = sorted(list(set(re.findall('@d ', rollback_sql))))
                    # 因为第一个列前面没有逗号或者and,所以单独替换
                    rollback_sql = rollback_sql.replace('@1=', result_dict[table_name][0][1] '=')
                    for col in col_list[1:]:
                        i = int(col[1:]) - 1
                        rollback_sql = rollback_sql.replace(col '=', 'AND '   result_dict[table_name][i][1] '=',1)
                    # 如果only_primary开启且存在主键,where条件里就只列出主键字段
                    if int(only_primary) == 1 and pri_dict[table_name] <> ():
                        sub_where = ''
                        for primary in pri_dict[table_name]:
                            primary_name = primary[1]
                            for condition in rollback_sql.split('WHERE', 1)[1].splitlines():
                                if re.compile('^s*' primary_name).match(condition) or re.compile('^s*ANDs*' primary_name).match(condition):
                                    sub_where = sub_where   condition   'n'
                        sub_where = re.sub('^s*AND', '', sub_where, 1)
                        rollback_sql = rollback_sql.split('WHERE', 1)[0]   'WHEREn'   sub_where
                if sql.split()[0] == 'UPDATE':
                    rollback_sql = re.sub('SETn', '#SET#n', sql, 1)
                    rollback_sql = re.sub('WHEREn', 'SETn', rollback_sql, 1)
                    rollback_sql = re.sub('#SET#n', 'WHEREn', rollback_sql, 1)
                    tablename_pos = 1
                    table_name = rollback_sql.split()[tablename_pos].replace('`', '')
                    # 获取该sql中的所有列
                    col_list = sorted(list(set(re.findall('@d ', rollback_sql))))
                    # 因为第一个列前面没有逗号或者and,所以单独替换
                    rollback_sql = rollback_sql.replace('@1=', result_dict[table_name][0][1]   '=')
                    for col in col_list[1:]:
                        i = int(col[1:]) - 1
                        rollback_sql = rollback_sql.replace(col '=', ','   result_dict[table_name][i][1] '=', 1).replace(col '=','AND '  result_dict[table_name][i][1] '=')
                    # 如果only_primary开启且存在主键,where条件里就只列出主键字段
                    if int(only_primary) == 1 and pri_dict[table_name] <> ():
                        sub_where = ''
                        for primary in pri_dict[table_name]:
                            primary_name = primary[1]
                            for condition in rollback_sql.split('WHERE', 1)[1].splitlines():
                                if re.compile('^s*'   primary_name).match(condition) or re.compile('^s*ANDs*' primary_name).match(condition):
                                    sub_where = sub_where   condition   'n'
                        sub_where = re.sub('^s*AND', '', sub_where, 1)
                        rollback_sql = rollback_sql.split('WHERE', 1)[0]   'WHEREn'   sub_where

                if sql.split()[0] == 'DELETE':
                    rollback_sql = re.sub('^DELETE FROM', 'INSERT INTO', sql, 1)
                    rollback_sql = re.sub('WHEREn', 'SETn', rollback_sql, 1)
                    tablename_pos = 2
                    table_name = rollback_sql.split()[tablename_pos].replace('`', '')
                    # 获取该sql中的所有列
                    col_list = sorted(list(set(re.findall('@d ', rollback_sql))))
                    # 因为第一个列前面没有逗号或者and,所以单独替换
                    rollback_sql = rollback_sql.replace('@1=', result_dict[table_name][0][1]   '=')
                    for col in col_list[1:]:
                        i = int(col[1:]) - 1
                        rollback_sql = rollback_sql.replace(col '=', ','   result_dict[table_name][i][1] '=',1)

                rollback_sql = re.sub('n$',';n',rollback_sql)
                #print rollback_sql
                fileOutput.write(rollback_sql)
            except IndexError,e:
                print "Error:%s" % str(e)
                sys.exit()
    print "done!"

def usage():
    help_info="""==========================================================================================
Command line options :
    --help                  # OUT : print help info
    -f, --binlog            # IN  : binlog file. (required)
    -o, --outfile           # OUT : output rollback sql file. (default 'rollback.sql')
    -h, --host              # IN  : host. (default '127.0.0.1')
    -u, --user              # IN  : user. (required)
    -p, --password          # IN  : password. (required)
    -P, --port              # IN  : port. (default 3306)
    --start-datetime        # IN  : start datetime. (default '1970-01-01 00:00:00')
    --stop-datetime         # IN  : stop datetime. default '2070-01-01 00:00:00'
    --start-position        # IN  : start position. (default '4')
    --stop-position         # IN  : stop position. (default '18446744073709551615')
    -d, --database          # IN  : List entries for just this database (No default value).
    --only-primary          # IN  : Only list primary key in where condition (default 0)

Sample :
   shell> python binlog_rollback.py -f 'mysql-bin.000001' -o '/tmp/rollback.sql' -h 192.168.0.1 -u 'user' -p 'pwd' -P 3307 -d dbname
=========================================================================================="""

    print help_info
    sys.exit()



if __name__ == '__main__':
    getopts_parse_binlog()
    init_col_name()
    gen_rollback_sql()

必赢娱乐 11

本文由bwin必赢发布,转载请注明来源

关键词: 随笔