• Xtrabackup备份MySQL


    使用mysqldump进行数据库或表的备份非常方便,操作简单使用灵活,在小数据量的备份和恢复时间可以接受,如果数据量较大,mysqldump恢复的时间会很长而难以接受。 xtrabackup是一款高效的备份工具,备份时并不会影响原数据库的正常更新

    xtrabackup安装

    mkdir xtrbackup; cd xtrbackup
    wget https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup-2.4.9/binary/redhat/7/x86_64/percona-xtrabackup-24-2.4.9-1.el7.x86_64.rpm
    wget https://www.percona.com/downloads/percona-toolkit/3.0.6/binary/redhat/7/x86_64/percona-toolkit-3.0.6-1.el7.x86_64.rpm
    yum install ./*.rpm 

    创建备份用户及目录

    #mysql备份用户
    GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT ON *.* TO 'bkuser'@'localhost' IDENTIFIED BY '123456';
    FLUSH PRIVILEGES;
    
    #备份目录
    mkdir /opt/bak/mysql -p

    全量备份

    1、备份阶段(备份文件会存储在一个以备份时间命名的子目录下)

    innobackupex --host=127.0.0.1 --port=3306 --user=bkuser --password=123456 /opt/bak/mysql/

    2、准备阶段

    创建备份后,备份数据还处于不可用状态。因为redo log中可能存在未提交的事务和已经提交的事务,需要通过准备阶段使备份数据达到一致状态;如果状体为'completed OK',则表明innobackupex执行完了所有所需的操作,数据达到一致状态

    innobackupex --apply-log /opt/bak/mysql/2018-01-13_14-07-05

    3、恢复阶段

    恢复的过程中,datadir目录必须为空,如果不为空,innobackupes --copy-back将不会复制

    cd /var/lib/mysql;rm -rf ./*

    在恢复过程中,数据库需要处于关闭的状态(导入部分备份除外)

    systemctl stop mysqld

    innobackupex将所有的数据相关的文件复制到服务器中的datadir目录

    innobackupex --copy-back --defaults-file=/opt/app/mysql/my.cnf /opt/bak/mysql/2018-01-13_14-07-05

    复制完成后,文件属性不会改变,大多数情况下,在启动MySQL数据库之前,需要修改文件的所有权

    chown -R mysql.mysql /var/lib/mysql

    启动mysql

    systemctl start mysqld

    建议与提醒

    • 请使用高版本的innobackupex
    • 建议远程备份
    • 尽量不要使用innbackupex自带的增量备份,恢复比较麻烦容易出错
    • 备份结束时,请立即apply-log,这样能够知道备份集是否可用,如果出问题,还可以立即再备份一次或报告出来

    #!/usr/bin/python
    # coding=utf-8
    # Author:heboan Date:2018-04-16
    
    import os
    import subprocess
    from datetime import date
    import re
    import tarfile
    import smtplib
    from email.mime.text import MIMEText
    
    class Bkdb:
        host = 'localhost'
        port = 3306
        user = 'bkuser'
        passwd = 'xxxxxx'
        socket = '/opt/app/mysql-5.6.34/tmp/mysql.sock'
        bk_paths = ['/opt/bak', '/opt/bak/tgz']
        bk_date = date.today().isoformat()   #2018-04-16
        bk_log = bk_paths[1] + '/' + bk_date + '.log' 
     
        def __create_bkdir(self, bk_paths):
            '''创建备份目录'''
            for bk_path in bk_paths:
                if not os.path.exists(bk_path):
                    os.makedirs(bk_path)
                    subprocess.call('chown -R heboan.heboan {}'.format(bk_path), shell=True)
    
        def __backup(self):
            '''备份'''        
            subprocess.call('innobackupex --host={0} 
                            --port={1} 
                            --user={2} 
                            --password={3} 
                            --socket={4} {5} >{6} 2>&1'.format(Bkdb.host, Bkdb.port, Bkdb.user, Bkdb.passwd, Bkdb.socket, Bkdb.bk_paths[0], Bkdb.bk_log),shell=True)
            with open(Bkdb.bk_log, 'r') as f:
                lines = f.readlines()
                last_line = lines[-1].strip('
    ')
                if last_line[-3:-1] == 'OK':
                    #print '备份完成'
                    return True
                else:
                    return False
    
        def __apply(self, is_ok):
            '''确定备份是否可用'''
            if is_ok:
                dirs = os.listdir(Bkdb.bk_paths[0])
                for dir in dirs:
                    if Bkdb.bk_date in dir:
                        subprocess.call('innobackupex 
                                        --apply-log {0}/{1} >{2} 2>&1'.format(Bkdb.bk_paths[0], dir, Bkdb.bk_log), shell=True)
                        with open(Bkdb.bk_log, 'r') as f:
                            lines = f.readlines()
                            last_line = lines[-1].strip('
    ')
                            if last_line[-3:-1] == 'OK':
                                #print '备份可用'
                                return dir
                            else:
                                return False
            else:
                return False
    
        def __tgz(self, dir_name):
            '''备份打包'''
            if dir_name:
                try:
                    os.chdir(Bkdb.bk_paths[0])
                    tgz_name = Bkdb.bk_paths[1] + '/mysql_' + dir_name + '.tar.gz'
                    with tarfile.open(tgz_name, 'w:gz') as f:
                        f.add(dir_name)
                    #print '打包成功'
                    subprocess.call('rm -rf {}'.format(Bkdb.bk_paths[0] + '/' + dir_name), shell=True)                
                    size = self.__formatSize(os.path.getsize(tgz_name))
                    return {"file_name": tgz_name, "file_size": size}
                except Exception as e:
                    return False
            else:
                return False
    
        def __sendemail(self, file_info):
            '''邮件通知'''
            HOST = 'smtp.xxxxx.com'  #发件邮箱的smtp
            SUBJECT = '数据库备份报告'
            To = ['xxxxx@qq.com,xxxx@7atour.com']  #收件人,可以写多个
            FROM = 'monitor@xxxx.com'   #发件邮箱
    
            if file_info:
                msg = MIMEText("""
                 <table width="800p" border="0" cellspacing="0", cellpadding="4">
                     <tr>
                         <td bgcolor="#CECFAD" height="20" style="font-size:14px"> 数据库备份报告</td>
                     </tr>
                     <tr>
                         <td bgcolor="#EFEBDE" height="100" style="font-size:13px">
                             备份文件名: {} <br>
                             备份文件大小: {} <br>
                             备份来源: pr-db-01(从库) <br>
                             备份状态: 成功
                         </td>
                     </tr>
                </table> """.format(file_info['file_name'], file_info['file_size']), "html", "utf-8")
            else:
                msg = MIMEText("""
                 <table width="800p" border="0" cellspacing="0", cellpadding="4">
                     <tr>
                         <td bgcolor="#CECFAD" height="20" style="font-size:14px"> 数据库备份报告</td>
                     </tr>
                     <tr>
                         <td bgcolor="#EFEBDE" height="100" style="font-size:13px">
                             备份状态: 失败
                         </td>
                     </tr>
                </table> """, "html", "utf-8")
    
            msg['Subject'] = SUBJECT
            msg['From'] = FROM
            msg['To'] = ';'.join(To)
            try:
                server = smtplib.SMTP_SSL(host=HOST, port=465)
                #server.connect(HOST, "25")
                #server.starttls()
                server.login("monitor@agentool.com", "bt6eJtLBPz")
                server.sendmail(FROM, To, msg.as_string())
                server.quit()
                #print '发送成功'
            except Exception as e:
                #print '发送失败: ' + str(e)
                pass
    
        def __formatSize(self, bytes):
            bytes = float(bytes)
            kb = bytes / 1024
            if kb >= 1024:
                M = kb /1024
                if M >= 1024:
                    G = M /1024
                    return "{} G".format(G)
                else:
                    return "{} M".format(M)
            else:
                return "{} K".format(kb)
    
        def go(self):
            self.__create_bkdir(Bkdb.bk_paths)
            is_ok = self.__backup()
            dir_name = self.__apply(is_ok)
            file_info = self.__tgz(dir_name)
            self.__sendemail(file_info)
    
    
    bkdb = Bkdb()
    bkdb.go()
    生产用的备份脚本
  • 相关阅读:
    Nginx+Tomcat 集群部署
    Android5.0新特性——CardView 使用
    Android-SQLite版本问题
    Android UI ListView的使用
    Android
    Android四大组件之Activity一(组件的概念、Intent、监听)
    JAVA内部类使用
    Android 第一个程序 及 环境搭配
    Android-AsyncTask异步任务(获取手机联系人)
    Android-Application
  • 原文地址:https://www.cnblogs.com/sellsa/p/9346951.html
Copyright © 2020-2023  润新知