解析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时导致大量数据错误恢复的办法 ...
随机推荐
- cocos2dx基础篇(22) 基本动画CCAnimation/CCAnimate
[小知识] CCSpriteFrame :精灵帧. 它是相对动画而产生的,其实就是一张纹理图片. CCAnimationFrame :动画帧. 由精灵帧与间隔帧数组成,是动画CC ...
- HTML协义代码
这些状态码被分为五大类: 100-199 用于指定客户端应相应的某些动作. 200-299 用于表示请求成功. 300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息. 400- ...
- java script 的注释与分号
// 单行注释 /**/多行注释 在js 中 变量.函数和操作符都是区分大小写的 什么是标识符 变量.函数.属性的名字.或者函数的参数. 变量的命名规范:不能以数字开头. 变量声明: var nam ...
- Akka系列(七):Actor持久化之Akka persistence
前言.......... 我们在使用Akka时,会经常遇到一些存储Actor内部状态的场景,在系统正常运行的情况下,我们不需要担心什么,但是当系统出错,比如Actor错误需要重启,或者内存溢出,亦或者 ...
- HDU 1171 Big Event in HDU (动态规划、01背包)
Big Event in HDU Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- spring boot-4.配置文件
官方文档的23.4章节介绍了关于配置文件的内容 springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的 ...
- [转帖]56核Xeon Platinum 9200现身 - 英特尔有史以来最大的CPU封装
56核Xeon Platinum 9200现身 - 英特尔有史以来最大的CPU封装 https://www.cnbeta.com/articles/tech/835271.htm 当英特尔宣布上周正式 ...
- springMVC原理简单介绍
说明: 用户发送请求到DispatcherServlet,即前端控制器 DipatcherServlet调用处理器映射器HandlerMapping解析 处理器映射器HandlerMapping根据请 ...
- python生成饼图解决中文乱码
解决乱码问题 乱码的原因 字体的不匹配 解决的方法 加上引用中文字体就好了 matplotlib.rcParams['font.sans-serif'] = ['SimHei'] 代码 def sta ...
- [POI2011]SMI-Garbage 题解
题面 想必各位大佬一定想到了把现在和目标值不一致的边加入到一个新建的图上: 问题就变为了在新的图上寻找有多少个欧拉回路,并输出这些路径: 我们可以用栈来记录情况,然后对于会回答稍微处理处理就好了: # ...