设为首页 收藏本站
查看: 437|回复: 0

[经验分享] 闪回脚本:mysql_rollback.py

[复制链接]

尚未签到

发表于 2018-10-2 07:03:30 | 显示全部楼层 |阅读模式
#!/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('SET\n', 'WHERE\n', 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][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*AND\s*'+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] + 'WHERE\n' + sub_where
  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)
  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][1]+'=', 1).replace(col+'=','AND ' +result_dict[table_name][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*AND\s*'+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] + 'WHERE\n' + sub_where
  if sql.split()[0] == 'DELETE':
  rollback_sql = re.sub('^DELETE FROM', 'INSERT INTO', sql, 1)
  rollback_sql = re.sub('WHERE\n', 'SET\n', 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][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()


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-607305-1-1.html 上篇帖子: 生产环境配置mysql主从复制 下篇帖子: MySQL取消权限和删除用户
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表