一套完整的mysql备份方案

LINUX IT敢客 3个月前 (05-22) 654次浏览 已收录 0个评论 扫描二维码

1.环境说明

    系统为 centos 6.5 或者更高

    需要安装 mutt 和 msmtp 并可以发送邮件,其他能发邮件也行

    需要安装 python 2.6.6,目前都是 python2.7 版本,也可以!

    需要安装 xtrabackup

2.备份方案功能模块介绍

一套完整的 mysql 备份方案

备份:

    使用 xtrabackup 进行备份,每次备份会把备份文件放到一个当前日期和时间的文件夹内。所以创建备份夹 new,把备份文件放到 new 中,并根据 new 中文件夹的个数判断是全备还是增备还是需要转移文件到 last 中。第一个文件是全备,每次增备是在前一天的基础上进行增备。备份脚本在把所有的文件从 new 移动到 last 的时候 会把所有文件文件打包。以下是 mysql 备份脚本:

#!/usr/bin/env python
#coding:utf-8
#auther:Bran Guo
#date:10/23/2015 
#description:myql 自动备份脚本,添加定时任务自动运行,不要修改 mysqlbackup 文件夹中的文件
#version:V1.0
 
import ConfigParser,os,datetime,logger
 
#读取配置文件
conf = ConfigParser.ConfigParser()
conf.read("mysqlbak.conf")
 
bakdir_new = conf.get("file_path","bakdir_new")
bakdir_last = conf.get("file_path","bakdir_last")
bak_cycle = conf.get('bak_cycle','bak_cycle')
bak_output = conf.get('log_file','bak_output')
bak_user = conf.get('mysql_set','bak_user')
bak_passwd = conf.get('mysql_set','bak_passwd')
os.environ['bakdir_new']=str(bakdir_new)
os.environ['bak_output']=str(bak_output)
os.environ['bak_user']=str(bak_user)
os.environ['bak_passwd']=str(bak_passwd)
  
#判断备份文件夹个数
dirnew_count = int(os.popen('ls %s |wc -l' % bakdir_new).read())
if dirnew_count == 0:
    os.system('echo %s full backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now())
    ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd $bakdir_new &>> $bak_output') 
    logger.logger(ret)
elif dirnew_count >= int(bak_cycle):
    os.system('rm -rf %s/*' % bakdir_last)
    os.system('mv %s/* %s' % (bakdir_new,bakdir_last))
    os.system('tar zcf %s/`date +%%m-%%d-%%Y`.tar.gz %s/*' %(bakdir_last,bakdir_last))
    os.system('echo %s full backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now())
    ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd $bakdir_new &>> $bak_output') 
    logger.logger(ret)
else:
        full_file = os.popen('ls %s' % bakdir_new).readlines()
        for file_name in full_file :
        file_list= []
        file_list.append(file_name)
        basedir = file_list[-1]
    os.system('echo %s incremental backup start ---------------------------------------------------- >> $bak_output ' % datetime.datetime.now())
    os.environ['basedir']=str(basedir)
    ret = os.system('innobackupex --user=$bak_user --password=$bak_passwd --incremental $bakdir_new --incremental-basedir=$bakdir_new/$basedir &>> $bak_output')
    logger.logger(ret)

还原:

    使用 xtrabackup 还原需要先准备(perpare)。一般情况下,在备份完成后,数据尚且不能用于恢复操作,因为备份的数据中可能会包含尚未提交的事务或已经提交但尚未同步至数据文件中的事务。因此,此时数据文件仍处理不一致状态。“准备”的主要作用正是通过回滚未提交的事务及同步已经提交的事务至数据文件也使得数据文件处于一致性状态。准备的过程是以第一个完备为基础,提交第二个然后是第三个一直到最后一个。整个准备完成后使用第一个完备文件进行还原即可。还原脚本使用 cli 进行交互,用户可以选择还原到最近日期或指定日期。

还原脚本运行界面

一套完整的 mysql 备份方案

还原脚本代码

#!/usr/bin/python
#coding:utf-8
#auther:Bran Guo
#date:10/26/2015
#version:V1.0
 
import os,ConfigParser
 
#读取配置文件
conf = ConfigParser.ConfigParser()
conf.read("mysqlbak.conf")
 
bakdir_new = conf.get('file_path','bakdir_new')
bakdir_last = conf.get('file_path','bakdir_last')
mysql_data_path = conf.get('mysql_set','mysql_data_path')
dirlist_new = os.popen('ls %s' % bakdir_new).readlines()
dirlist_last = os.popen('ls %s' % bakdir_last).readlines()
 
#备份函数
def restore(dir_count=len(dirlist_new),bakdir=bakdir_new,dirlist=dirlist_new):
    if dir_count == 1:
        print dir_count,bakdir,dirlist
        ret=os.system('innobackupex --apply-log --redo-only %s/%s' %(bakdir,dirlist[0]))       
        if ret != 0:
            print "prepare failed"
            exit()
    else:
        ret=os.system('innobackupex --apply-log --redo-only %s/%s' %(bakdir,dirlist[0]))       
        count = 1
        while (count < dir_count):
            incrdir = dirlist[count]
            basedir = dirlist[0]
            os.environ['incrdir'] = str(incrdir)
            os.environ['basedir'] = str(basedir)
            os.environ['bakdir'] = str(bakdir)
            ret=os.system('innobackupex --apply-log --redo-only $bakdir/$basedir  --incremental-dir=$bakdir/$incrdir')
            if ret != 0:
                print "prepare failed"
            count +=1 
    os.system('service mysqld stop')
    os.system('rm -rf %s' % mysql_data_path)
    os.system('innobackupex --copy-back %s/%s' %(bakdir,dirlist[0]))    
    os.system('chown -R mysql:mysql %s' % mysql_data_path)
    os.system('service mysqld start')
 
#输入菜单
while True:
    user_input = raw_input('Command (m for help):').strip()
    if user_input == 'm':
        print '''Warning: The following command will remove mysql datafile, should be used with caution.
    r   restore to recent backup
    s   show backup list
    n   choose backup restore from new
    l   choose backup restore from last
    q   quit
''',
    elif user_input == 'r':
        restore()
    elif user_input == 'q':
        exit()
    elif user_input == 's':
        print 'New:'
        os.system('ls %s' % bakdir_new)
        print 'Last'
        os.system('ls %s' % bakdir_new)
    elif user_input == 'n':
        os.system('ls -l %s' % bakdir_new)
        while True:
            user_input = raw_input('Please enter line number restore:').strip()
            if user_input == 'q':
                exit()
            try:
                line_number = int(user_input)
                dir_count = len(dirlist_new)
                if line_number <= dir_count:
                    restore(line_number)
                else:
                    print '''Please enter a number less then line or "q".'''
            except ValueError:
                print '''Please enter a number less then line or "q".'''
    elif user_input == 'l':
        os.system('ls -l %s' % bakdir_last)
        while True:
            user_input = raw_input('Please enter line number restore:').strip()
            if user_input == 'q':
                exit()
            try:
                line_number = int(user_input)
                dir_count = len(dirlist_last)
                if line_number <= dir_count:
                    restore(line_number,bakdir_last,dirlist_last)
                else:
                    print '''Please enter a number less then line sum or "q".''' 
            except ValueError:
                print '''Please enter a number less then line sum "q".'''

远程保存:

    使用 rsync 和 scp 同步文件(脚本在备份服务器)

备份脚本 rsync

#!/bin/bash
rsync -Paz -e 'ssh -p 22'  xxx@106.xxx.xxx.xxx:/home/dev/mysqlbackup/new ./ &> ./logs/rsyncbak.log

备份脚本 scp

#!/bin/bash
scp -P 22 xxx@106.xxx.xxx.xxx:/home/dev/mysqlbackup/last/*.tar.gz ./history &> ./logs/scpbak.log

日志:

    所有 xtrabackup 的输出会保存到一个日志文件便于排查。通过命令执行后返回值判断成功或失败后的结果输出到一个日志文件中。

#!/usr/bin/python
 
import datetime,os,ConfigParser
 
conf = ConfigParser.ConfigParser()
conf.read("mysqlbak.conf")
bak_log = conf.get("log_file","bak_log")
mail_addr = conf.get('mail','mail_addr')
 
logfile = 'backup.log'
def logger(ret):
    if ret == 0:
        echo_line = "%s\tBackup mysql data file succes\n" % datetime.datetime.now()
    else:
        echo_line = "%s\tBackup mysql data file failed, plz check bakoutput.log\n" % datetime.datetime.now()
        os.system('echo "%s" | mutt -s "Backup mysql failed" %s' %(echo_line,mail_addr))
    f = file(bak_log,'a')
    f.write(echo_line)
    f.flush()
    f.close()
 

邮件:

    每次备份失败后背自动发送邮件给指定邮件地址,便于运维及时发现问题并进行排查。发送邮件功能写到日志脚本中

配置文件:

    除远程保存部分脚本(放在备份服务器)外,所有变量都被抽出来放到配置文件中。

#备份文件路径
[file_path]
bakdir_new = /home/dev/mysqlbackup/new
bakdir_last = /home/dev/mysqlbackup/last
 
#备份周期
[bak_cycle]
bak_cycle = 7
 
#日志文件位置
[log_file]
bak_output = bakoutput.log
bak_log = backup.log
 
#MySQL 设置
[mysql_set]
mysql_data_path = /var/lib/mysql
bak_user = user
bak_passwd = password
 
#备份失败发送邮件地址
[mail]
mail_addr = xxxxxx@xxx.com

IT 敢客 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:一套完整的 mysql 备份方案
喜欢 (5)
[313176056@qq.com]
分享 (0)
IT敢客
关于作者:
“我所做的一切都是为了方便我的生活~~~“
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址