xtrabackup工具介紹1、完全備份# innobackupex --user=DBUSER --password=DBUSERPASS /path/to/BACKUP-DIR/如果要使用一個最小許可權的用戶進行備份,則可基於如下命令創建此類用戶:(不好用)mysql> CREATE USER '... ...
xtrabackup
工具介紹
1、完全備份
# innobackupex --user=DBUSER --password=DBUSERPASS /path/to/BACKUP-DIR/
如果要使用一個最小許可權的用戶進行備份,則可基於如下命令創建此類用戶:(不好用)
mysql> CREATE USER 'backupupser'@'localhost' IDENTIFIED BY 'backupuser';
mysql> GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backupuser'@'localhost';
mysql> FLUSH PRIVILEGES;
使用innobakupex備份時,其會調用xtrabackup備份所有的InnoDB表,複製所有關於表結構定義的相關文件(.frm)、以及MyISAM、MERGE、CSV和ARCHIVE表的相關文件,同時還會備份觸發器和資料庫配置信息相關的文件。這些文件會被保存至一個以時間命令的目錄中。
在備份的同時,innobackupex還會在備份目錄中創建如下文件:
(1)xtrabackup_checkpoints ―― 備份類型(如完全或增量)、備份狀態(如是否已經為prepared狀態)和LSN(日誌序列號)範圍信息;
每個InnoDB頁(通常為16k大小)都會包含一個日誌序列號,即LSN。LSN是整個資料庫系統的系統版本號,每個頁面相關的LSN能夠表明此頁面最近是如何發生改變的。
(2)xtrabackup_binlog_info ―― mysql伺服器當前正在使用的二進位日誌文件及至備份這一刻為止二進位日誌事件的位置。
(3)xtrabackup_binlog_pos_innodb ―― 二進位日誌文件及用於InnoDB或XtraDB表的二進位日誌文件的當前position。
(4)xtrabackup_binary ―― 備份中用到的xtrabackup的可執行文件;
(5)backup-my.cnf ―― 備份命令用到的配置選項信息;
在使用innobackupex進行備份時,還可以使用--no-timestamp選項來阻止命令自動創建一個以時間命名的目錄;如此一來,innobackupex命令將會創建一個BACKUP-DIR目錄來存儲備份數據。
2、準備(prepare)一個完全備份
一般情況下,在備份完成後,數據尚且不能用於恢復操作,因為備份的數據中可能會包含尚未提交的事務或已經提交但尚未同步至數據文件中的事務。因此,此時數據文件仍處理不一致狀態。“準備”的主要作用正是通過回滾未提交的事務及同步已經提交的事務至數據文件也使得數據文件處於一致性狀態。
innobakupex命令的--apply-log選項可用於實現上述功能。如下麵的命令:
# innobackupex --apply-log /path/to/BACKUP-DIR
如果執行正確,其最後輸出的幾行信息通常如下:
xtrabackup: starting shutdown with innodb_fast_shutdown = 1
120407 9:01:36 InnoDB: Starting shutdown...
120407 9:01:40 InnoDB: Shutdown completed; log sequence number 92036620
120407 09:01:40 innobackupex: completed OK!
在實現“準備”的過程中,innobackupex通常還可以使用--use-memory選項來指定其可以使用的記憶體的大小,預設通常為100M。如果有足夠的記憶體可用,可以多劃分一些記憶體給prepare的過程,以提高其完成速度。
3、從一個完全備份中恢複數據
innobackupex命令的--copy-back選項用於執行恢復操作,其通過複製所有數據相關的文件至mysql伺服器DATADIR目錄中來執行恢復過程。innobackupex通過backup-my.cnf來獲取DATADIR目錄的相關信息。
# innobackupex --copy-back /path/to/BACKUP-DIR
如果執行正確,其輸出信息的最後幾行通常如下:
innobackupex: Starting to copy InnoDB log files
innobackupex: in '/backup/2012-04-07_08-17-03'
innobackupex: back to original InnoDB log directory '/mydata/data'
innobackupex: Finished copying back files.
120407 09:36:10 innobackupex: completed OK!
請確保如上信息的最行一行出現“innobackupex: completed OK!”。
當數據恢復至DATADIR目錄以後,還需要確保所有數據文件的屬主和屬組均為正確的用戶,如mysql,否則,在啟動mysqld之前還需要事先修改數據文件的屬主和屬組。如:
# chown -R mysql:mysql /mydata/data/
增量備份還原
需要註意的是,增量備份僅能應用於InnoDB或XtraDB表,對於MyISAM表而言,執行增量備份時其實進行的是完全備份。
“準備”(prepare)增量備份與整理完全備份有著一些不同,尤其要註意的是:
(1)需要在每個備份(包括完全和各個增量備份)上,將已經提交的事務進行“重放”。“重放”之後,所有的備份數據將合併到完全備份上。
(2)基於所有的備份將未提交的事務進行“回滾”。
於是,操作就變成了:
# innobackupex --apply-log --redo-only BASE-DIR
接著執行:
# innobackupex --apply-log --redo-only BASE-DIR --incremental-dir=INCREMENTAL-DIR-1(必須為絕對路徑)
而後是第二個增量:
# innobackupex --apply-log --redo-only BASE-DIR --incremental-dir=INCREMENTAL-DIR-2
其中BASE-DIR指的是完全備份所在的目錄,而INCREMENTAL-DIR-1指的是第一次增量備份的目錄,INCREMENTAL-DIR-2指的是第二次增量備份的目錄,其它依次類推,即如果有多次增量備份,每一次都要執行如上操作;
例如:
innobackupex --apply-log --redo-only /backup/xtrabackup/2016-10-01_17-00-13/
innobackupex --apply-log --redo-only /backup/xtrabackup/2016-10-01_17-00-13/ --incremental-dir=/backup/xtrabackup/2016-10-01_17-15-00/
'''
腳本內容
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author EdwardLiu
import datetime
import os
from subprocess import Popen, PIPE
import logging
# 獲取當前時間
time = datetime.datetime.now().strftime("%Y-%m-%d-%H")
# 定義備份存放目錄
backup_directory = "/software/mysql_backup/"
# 定義完全備份目錄
full_dir = "%s" % backup_directory + "full" + "backup"+"/"
# 定義增量備份目錄
increment_dir = "%s" % backup_directory + "increment" + "/"
# 命令路徑
EXES = "/usr/bin/innobackupex"
EXES_increment = "/usr/bin/innobackupex"
# 備份使用的用戶密碼
mysql_user = "root"
mysql_password = "comall2014"
# 備份使用命令
backup_full_mysql_command = "%s --user=%s --password=%s %s" % (EXES, mysql_user, mysql_password, full_dir)
class sunday:
def backup_command(self):
# 運行完整備份
proc = Popen(backup_full_mysql_command, stdin=PIPE, stdout=PIPE, universal_newlines=True, shell=True)
result = ""
exit_code = proc.wait()
if exit_code != 0:
for line in proc.stderr:
result = result + line
else:
for line in proc.stdout:
result = result + line
return result
def full_backup_dir(self):
# 判斷存放完整備份目錄是否存在
if not os.path.exists(full_dir):
os.makedirs(full_dir)
self.backup_command()
else:
self.backup_command()
def full_backup_run(self):
# 判斷備份目錄是否存在
if not os.path.exists(backup_directory):
os.makedirs(backup_directory)
self.full_backup_dir()
else:
self.full_backup_dir()
class Tuesday_Saturday:
# 每次的增量部署拿上一次的增量部署進行備份
def last_time_increment_name(self):
last_backup_directory_name = "ls -lrt %s | grep ^d | awk '{print $9}' | tail -n 1" % increment_dir
proc = Popen(last_backup_directory_name, stdin=PIPE, stdout=PIPE, universal_newlines=True, shell=True)
exit_code = proc.wait()
result = ""
if exit_code != 0:
for line in proc.stderr:
result = result + line
else:
for line in proc.stdout:
result = result + line
return result
def increment_every_backup(self):
# 根據最後一次完整備份做增量備份
last_full_dir = "%s/%s" % (increment_dir, self.last_time_increment_name())
backup_increment_mysql_command = "%s --user=%s --password=%s --incremental %s --incremental-basedir=%s" % (
EXES_increment, mysql_user, mysql_password, increment_dir, last_full_dir)
proc = Popen(backup_increment_mysql_command, stdin=PIPE, stdout=PIPE, universal_newlines=True, shell=True)
exit_code = proc.wait()
result = ""
if exit_code != 0:
for line in proc.stderr:
result = result + line
else:
for line in proc.stdout:
result = result + line
return result
def increment_run(self):
# 周2-周六使用增量備份進行增量備份
if not os.path.exists(increment_dir):
os.makedirs(increment_dir)
self.increment_every_backup()
else:
self.increment_every_backup()
class Monday:
def last_full_name(self):
# 最後一次完整備份文件名
last_backup_directory_name = "ls -lrt %s | grep ^d | awk '{print $9}' | tail -n 1" % full_dir
proc = Popen(last_backup_directory_name, stdin=PIPE, stdout=PIPE, universal_newlines=True, shell=True)
exit_code = proc.wait()
result = ""
if exit_code != 0:
for line in proc.stderr:
result = result + line
else:
for line in proc.stdout:
result = result + line
return result
def increment_run(self):
# 根據最後一次完整備份做增量備份
last_full_dir = "%s/%s" % (full_dir, self.last_full_name())
backup_increment_mysql_command = "%s --user=%s --password=%s --incremental %s --incremental-basedir=%s" % (
EXES_increment, mysql_user, mysql_password, increment_dir, last_full_dir)
proc = Popen(backup_increment_mysql_command, stdin=PIPE, stdout=PIPE, universal_newlines=True, shell=True)
exit_code = proc.wait()
result = ""
if exit_code != 0:
for line in proc.stderr:
result = result + line
else:
for line in proc.stdout:
result = result + line
return result
def increment(self):
if not os.path.exists(increment_dir):
os.makedirs(increment_dir)
self.increment_run()
else:
self.increment_run()
if __name__ == '__main__':
d = datetime.datetime.now()
if d.weekday() == 6:
print u'今天是星期%s'.encode('utf-8')%(d.weekday()+1)+u'執行全量備份'.encode('utf-8')
full = sunday()
full.full_backup_run()
elif d.weekday() == 0:
print u'今天執行上一次的全量的增量備份'.encode('utf-8')+u'星期%s'.encode('utf-8')%(d.weekday()+1)
zl_full = Monday()
zl_full.increment()
elif d.weekday() >=1 and d.weekday() < 6:
print u'今天是星期%s'.encode('utf-8')%(d.weekday()+1)+u'執行增量的增量備份'.encode('utf-8')
zl = Tuesday_Saturday()
zl.increment_run()
else:
print u'碰見鬼了,難道今天是星期8?????'.encode('utf-8')