数据操作过程中,误执行了delete 操作,现在需要将这些数据恢复
步骤如下:
1.首先需要在数据库的ROW模式下通过binlog提取SQL语句
这里我将这些数据保存为 binlog_02.sql这样一个文本文件,内容如下
binlog模式分三种(row,statement,mixed)
1.Row
日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改,只记录要修改的数据,只有value,不会有sql多表关联的情况。
优点:在row模式下,bin-log中可以不记录执行的sql语句的上下文相关的信息,仅仅只需要记录那一条记录被修改了,修改成什么样了,所以row的日志内容会非常清楚的记录下每一行数据修改的细节,非常容易理解。而且不会出现某些特定情况下的存储过程和function,以及trigger的调用和出发无法被正确复制问题。
缺点:在row模式下,所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。
2.statement
每一条会修改数据的sql都会记录到master的binlog中,slave在复制的时候sql进程会解析成和原来master端执行多相同的sql再执行。
有点:在statement模式下首先就是解决了row模式的缺点,不需要记录每一行数据的变化减少了binlog日志量,节省了I/O以及存储资源,提高性能。因为他只需要激励在master上所执行的语句的细节一届执行语句时候的上下的信息。
缺点:在statement模式下,由于他是记录的执行语句,所以,为了让这些语句在slave端也能正确执行,那么他还必须记录每条语句在执行的时候的一些相关信息,也就是上下文信息,以保证所有语句在slave端被执行的时候能够得到和在master端执行时候相同的结果。另外就是,由于mysql现在发展比较快,很多的新功能不断的加入,使mysql的复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug也就越容易出现。在statement中,目前已经发现不少情况会造成Mysql的复制出现问题,主要是修改数据的时候使用了某些特定的函数或者功能的时候会出现,比如:sleep()函数在有些版本中就不能被正确复制,在存储过程中使用了last_insert_id()函数,可能会使slave和master上得到不一致的id等等。由于row是基于每一行来记录的变化,所以不会出现,类似的问题。
3.Mixed
从官方文档中看到,之前的 MySQL 一直都只有基于 statement 的复制模式,直到 5.1.5 版本的 MySQL 才开始支持 row 复制。从 5.0 开始,MySQL 的复制已经解决了大量老版本中出现的无法正确复制的问题。但是由于存储过程的出现,给 MySQL Replication 又带来了更大的新挑战。另外,看到官方文档说,从 5.1.8 版本开始,MySQL 提供了除 Statement 和 Row 之外的第三种复制模式:Mixed,实际上就是前两种模式的结合。在 Mixed 模式下,MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种。新版本中的 statment 还是和以前一样,仅仅记录执行的语句。而新版本的 MySQL 中对 row 模式也被做了优化,并不是所有的修改都会以 row 模式来记录,比如遇到表结构变更的时候就会以 statement 模式来记录,如果 SQL 语句确实就是 update 或者 delete 等修改数据的语句,那么还是会记录所有行的变更。
2.使用Python脚本将delete改为insert
# -*- coding: utf-8 -*-
import re
# 列表拆分
def div_list(ls,n):
n = int(n)
ls_len = len(ls)
j = ls_len/n
ls_return = []
for i in range(0,j):
ls_return.append(ls[i*n:(n*(i+1))])
return ls_return
# col_count为表的列数+2,binlog为mysqlbinlog -v处理过后的文本
def exc_binlog(binlog,col_count):
with open(binlog) as f:
lines=[]
for line in f:
if re.search('###',line):
line=re.sub("(d+)","",re.sub("@d+=",",",line.strip().replace("### ","").replace("DELETE FROM ",";INSERT INTO ").replace("WHERE"," SELECT")))
lines.append(line)
list=div_list(lines,col_count)
with open(r"D:\py\sql\binlog_02.sql",'w') as f:
for i in range(0,len(list)):
list1=list[i]
line=" ".join(list1).replace("SELECT ,","SELECT ")+" "
f.writelines(line)
if __name__ == '__main__':
import sys
exc_binlog(sys.argv[1],sys.argv[2])
有来个方法不了解:
1.python re模块 sub方法介绍 http://blog.csdn.net/yangchao228/article/details/6627855
2.sys.argv 这里复制了 这里方法的说明
教程里的源码我抄下来:
#!/usr/bin/python
# Filename: using_sys.py
import sys
print 'The command line arguments are:'
for i in sys.argv:
print i
print '
The PYTHONPATH is', sys.path, '
'
除去注释的第一行:
import sys
你include了iostream才能系统给你的std::cout;你import了sys,才能使用系统给你的sys.argv。
然后再说说argv这个变量。
「argv」是「argument variable」参数变量的简写形式,一般在命令行调用的时候由系统传递给程序。
这个变量其实是一个List列表,argv[0] 一般是被调用的脚本文件名或全路径,和操作系统有关,argv[1]和以后就是传入的数据了。
然后我们再看调用脚本的命令行:
python using_sys.py we are argumentspython就不用说了,「using_sys.py」脚本名,后面的用空格分割开的「we」「are」「argument」就是参数了。
PS.一般参数由空格分隔,如果参数内部有空格要使用英文双引号引起来比如这样:
python using_sys.py hi "I'm 7sDream"
按照教程的命令行运行脚本的时候,按照argv的定义:
argv = ["using_sys.py", "we", "are", "argument"]
3.执行python
这里需要传参数,所以不能直接在pycharm中直接执行py文件, 进入cmd命令行
输入 : python deletetoinsert.py d:pysqlinlog_02.sql 6
这里参数 d:pysqlinlog_02.sql 指需要替换内容的文件位置
6指一行显示的内容数量
执行完之后,效果如图所示:
这里有可能会出现一些意外状况 比如select ,没办法匹配,需要自己注意改一下,其他的应该就没什么问题了