解析binlog生成MySQL回滚脚本
如果数据库误操作想恢复数据。可以试试下面这个脚本。前提是执行DML操作。
#!/bin/env python
#coding:utf-8
#Author: Hogan
#Descript : 解析binlog生成MySQL回滚脚本 import getopt
import sys
import os
import re
import pymysql # 设置默认值
host = '127.0.0.1'
port = 3306
user = ''
password = ''
start_datetime = '1971-01-01 00:00:00'
stop_datetime = '2037-01-01 00:00:00'
start_position = ''
stop_position = ''
database = ''
mysqlbinlog = 'mysqlbinlog -v --base64-output=decode-rows '
binlogfile = ''
output = 'rollback.sql' # 提示信息
def usage():
help_info="""==========================================================================================
Command line options :
--help # OUT : print help info
-f, --binlogfile # 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 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() # 获取参数,生成binlog解析文件
def getops_parse_binlog():
global host
global user
global password
global port
global database
global start_datetime
global stop_datetime
global start_position
global stop_position
global binlogfile
global only_primary
global fileContent
global output try:
options, args = getopt.getopt(sys.argv[1:], "f:o:h:P:u:p:d", ["help", "binlogfile=","--output=","host=","port=","user=","password=","database=","start-datetime=",
"stop-datetime=","start-position=","stop-position=","only-primary="])
except getopt.GetoptError:
print('参数错误!')
options = []
if options == [] or 'help' in options[0][0]:
usage()
sys.exit()
print("正在获取参数......")
# print(options)
for name, value in options:
if name in ('-f', '--binlogfile='):
binlogfile = value
if name in ('-o', '--output='):
output = value
if name in ('-h', '--host='):
host = value
if name in ('-P', '--port='):
port = value
if name in ('-u', '--user='):
user = value
if name in ('-p', '--password='):
password = value
if name in ('-d', '--database='):
database = 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 == '--only-primary':
only_primary = value
if not binlogfile:
print("错误:请指定binlog文件名")
usage()
if not user:
print("错误:请指定用户名!")
usage()
if not password:
print("错误:请指定密码!")
usage()
if database:
condition_database = "--database='" + database + "'"
else:
condition_database = ''
print("正在解析binlog......")
cmd = ("%s --start-position=%s --stop-position=%s --start-datetime='%s' --stop-datetime='%s' %s %s| grep '###' -B 2 | sed -e 's/### //g' | sed -e 's/^INSERT/##INSERT/g' -e 's/^UPDATE/##UPDATE/g'\
-e 's/^DELETE/##DELETE/g'" % (mysqlbinlog, start_position, stop_position, start_datetime, stop_datetime, binlogfile, condition_database )) fileContent = os.popen(cmd).read() # 初始化binlog里的表名和列名,用全局字典result_dict来存储表名,列名
def init_clo_name():
global result_dict
global col_dict
result_dict = {}
# 统计binlog中出现的所有库名.表名
table_list = list(set(re.findall('`.*`\\.`.*`', fileContent))) for table in table_list:
db_name = table.split('.')[0].strip('`')
table_name = table.split('.')[1].strip('`')
# 连接数据库获取字段id
try:
conn = pymysql.connect(host=host, port=int(port), user=user, password=password)
cursor = conn.cursor()
# 获取字段名,字段position
cursor.execute("select ordinal_position, column_name from information_schema.columns where table_schema='%s' and table_name='%s'" %(db_name,table_name))
result = cursor.fetchall()
if result == ():
print('Warning: ' + db_name + '.' + table_name + '已删除')
result_dict[db_name+'.'+table_name] = result
except pymysql.Error as e:
try:
print("Error %d:%s" % (e.args[0], e.args[1]))
except IndexError:
print("MySQL Error:%s" % str(e))
sys.exit() # 拼接反向生成回滚SQL
def gen_rollback_sql():
# 打开输出文件
fileOutput = open(output, 'w')
print('正在拼凑SQL......')
# 将binlog解析的文件通过'--'进行分割,每块代表一个sql
area_list = fileContent.split('--\n')
# 逆序读取分块
for area in area_list[::-1]:
sql_list = area.split('##')
for sql_head in sql_list[0].splitlines():
sql_head = '#' + sql_head + '\n'
fileOutput.write(sql_head)
# 逐条对SQL进行替换更新,逆序
for sql in sql_list[::-1][:-1]:
try:
# 对insert语句进行拼接
if sql.split()[0] == 'INSERT':
rollback_sql = re.sub('^INSERT INTO', 'DELETE FROM', sql, 1)
rollback_sql = re.sub('SET\n' , 'WHERE\n', rollback_sql, 1)
table_name = rollback_sql.split()[2].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:]:
col_int = int(col[1:]) -1
rollback_sql = rollback_sql.replace(col, 'and '+ result_dict[table_name][col_int][1],1 ) #对update语句进行拼接
if sql.split()[0] == 'UPDATE':
rollback_sql = re.sub('SET\n', '#SET#\n', sql, 1)
rollback_sql = re.sub('WHERE\n', 'SET\n', rollback_sql, 1)
rollback_sql = re.sub('#SET#\n', 'WHERE\n',rollback_sql, 1)
table_name = rollback_sql.split()[1].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:]:
col_int = int(col[1:]) -1
rollback_sql = rollback_sql.replace(col, ','+ result_dict[table_name][col_int][1],1 ).replace(col,'and '+result_dict[table_name][col_int][1]) # 对delete语句进行拼接
if sql.split()[0] == 'DELETE':
rollback_sql = re.sub('^DELETE FROM', 'INSERT INTO', sql, 1)
rollback_sql = re.sub('WHERE', 'SET', rollback_sql, 1)
table_name = rollback_sql.split()[2].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:]:
col_int = int(col[1:]) -1
rollback_sql = rollback_sql.replace(col, ', '+ result_dict[table_name][col_int][1],1 )
#SQL结尾加;
rollback_sql = re.sub('\n$', ';', rollback_sql)
rollback_sql = re.sub('\n', '', rollback_sql)
rollback_sql = re.sub(';', ';\n', rollback_sql)
fileOutput.write(rollback_sql)
except IndexError as e:
print ("Error:%s" % str(e))
sys.exit()
print ("done!") if __name__ == '__main__':
getops_parse_binlog()
init_clo_name()
gen_rollback_sql()
解析binlog生成MySQL回滚脚本的更多相关文章
- 【MySQL】MySQL事务回滚脚本
MySQL自己的 mysqlbinlog | mysql 回滚不好用,自己写个简单脚本试试: 想法是用mysqlbinlog把需要回滚的事务区域从mysql-bin.file中找到,然后通过脚本再插入 ...
- 【MySQL】MySQL回滚工具
1.mysqlbinlog把事务从binlog中导出 2.从导出的binlog中找到要回滚的事务,去掉第一个DML语句前和最后一个DML语句后与DML无关的binlog信息 3.在目录中新建一个tab ...
- 【linux】【jenkins】jenkins构建、mvn或者npm打包、docker运行、失败自动回滚脚本
小白对jenkins运维的使用有点简单的想法,这里开个记录贴记录下. 由于未找到jenkins构建失败后执行其他脚本的插件,也暂时没有使用其他运维工具.所以想自己写一个shell脚本,一是方便其他人使 ...
- mysql回滚日志
一.回滚日志(undo log) 1.作用 保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读 2.内容 逻辑格式的日志,在执行undo的时候 ...
- 关于MySQL回滚机制
在事务中,每个正确的原子操作都会被顺序执行,直到遇到错误的原子操作,此时事务会将之前的操作进行回滚.回滚的意思是如果之前是插入操作,那么会执行删 除插入的记录,如果之前是update操作,也会执行up ...
- .net中使用mysql回滚和sqlserver回滚的区别
关于sqlserver事务和mysql事务 首先这是一种方法 public static int GetExecteQuery() { SqlConnection ...
- shell自动化一键部署脚本,秒级一键回滚脚本
#!/bin/bash # Node List PRE_LIST="192.168.222.163" # 预生产环境节点 GROUP1_LIST= ROLLBACK_LIST=&q ...
- SQL SERVER 生成MYSQL建表脚本
/****** Object: StoredProcedure [dbo].[GET_TableScript_MYSQL] Script Date: 06/15/2012 13:05:14 ***** ...
- 误删数据库怎么办?mysql 回滚,撤销操作,恢复数据
刚刚不小心把数据库删掉了,于是想着上网上找找有没有可以恢复数据库的方法,没想到还真有,除了备份以外,还有以下方法. 在mysql有时执行了错误的update或者delete时导致大量数据错误恢复的办法 ...
随机推荐
- pip install dal 失败问题
这个问题是我在看一本<Django企业开发实战>运行其中一个项目时遇到的 作为一个自学的python 这种问题挺头疼的 这不是代码逻辑的问题 没法像Debug 一样去找问题 我们能依据的 ...
- redis缓存与数据一致性
目录 缓存 缓存穿透 缓存雪崩(缓存失效) 缓存击穿(热点key) 缓存并发竞争(并发set) 数据一致性 缓存(双写)一致性 Redis集群(Redis-cluster)一致性原理 哨兵(Senti ...
- java中String中的endsWith()方法
解释:endsWith() ——此方法测试字符串是否以指定的后缀 suffix 结束. 此方法的定义:public boolean endsWith(String suffix) 我这里判断的是路径是 ...
- SQL中前置0和后置0的处理问题
在sql语句中经常遇到处理前置和后置数据的问题 1.首先使用convert转化函数对预处理的数据进行转化,CONVERT()函数可以将制定的数据类型转换为另一种数据类型 MySQL 的CAST()和C ...
- vue-loader介绍和单页组件介绍
$ \es6\sing-file> npm install vue-loader@14.1.1 -D vue-template-compiler@2.5.17 -D npm install v ...
- Java计算两个时间的天数差与月数差 LocalDateTime
/** * 计算两个时间点的天数差 * @param dt1 第一个时间点 * @param dt2 第二个时间点 * @return int,即要计算的天数差 */ public stat ...
- 利用lambda和条件表达式构造匿名递归函数
from operator import sub, mul def make_anonymous_factorial(): """Return the value of ...
- 常用的一些js校验
参考:https://blog.csdn.net/xxjmlgb/article/details/49467717 var valNull;//非空校验 var valSpecial;//特殊字符校验 ...
- easyui,获取tabs标签页内容,以及刷新datagrid
因为先点开tab页xxx查看表格,再点另一个tab页xxxx修改部分信息,再切换到tab页xxx,tab页xxx里的datagrid表格不会刷新,显示不了修改的信息(在此tab页按F5刷新可以解决,但 ...
- vue 条件渲染 v-if v-show
1.要点 1.1 v-if 条件性地渲染一块内容 <h1 v-if="awesome">Vue is awesome!</h1> 附带 / v- ...