若是數據庫誤操做想恢復數據。能夠試試下面這個腳本。前提是執行DML操做。python
#!/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 = '4' stop_position = '18446744073709551615' 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()