MySQL備份與恢復


MySQL備份與恢復

一 數據備份介紹

1.1 為何要備份

在生產環境中我們數據庫可能會遭遇各種各樣的不測從而導致數據丟失, 大概分為以下幾種.

  • 硬件故障
  • 軟件故障
  • 自然災害
  • 黑客攻擊
  • 誤操作 (占比最大)

須知在生產環境中,服務器的硬件壞了可以維修或者換新,軟件崩潰可以修復或重新安裝, 但是如果數據沒了那可就毀了,生產環境中最重要的應該就是數據了。所以, 為了在數據丟失之后能夠恢復數據, 我們就需要定期的備份數據。

ps:

在之前的章節里我們介紹過innodb存儲引擎有自動的數據恢復功能,在執行一條寫操作並且commit成功時innodb存儲引擎會將新數據其寫入redo log,此時如果數據庫掛掉,重啟后仍然可以依據redo log來恢復尚未執行完畢的數據,這跟本節講的數據備份與恢復是兩回事,試問,如果你一張表的數據不小心刪掉了,redo log能幫你恢復嗎,no,它只能幫你完成你尚未完成的操作,而對於你已經完成的操作,它幫不了你。所以不要混淆,為了防止數據意外丟失,我們還是應該做好定期的數據備份工作。

1.2 備份什么

我們要備份什么?

一般情況下, 我們需要備份的數據分為以下幾種

  • 數據
  • 二進制日志, InnoDB事務日志
  • 代碼(存儲過程、存儲函數、觸發器、事件調度器)
  • 服務器配置文件

1.3 備份的類型

1)冷備、溫備、熱備

按照備份時數據庫的運行狀態,可以分為三種

1)冷備:停庫、停服務來備份
即當數據庫進行備份時, 數據庫不能進行讀寫操作, 即數據庫要下線

2)溫備:不停庫、不停服務來備份,會(鎖表)阻止用戶的寫入	
即當數據庫進行備份時, 數據庫的讀操作可以執行, 但是不能執行寫操作 

3)熱備(建議):不停庫、不停服務來備份,也不會(鎖表)阻止用戶的寫入
即當數據庫進行備份時, 數據庫的讀寫操作均不是受影響 

MySQL中進行不同類型的備份還要考慮存儲引擎是否支持

  • MyISAM

    熱備 ×

    溫備 √

    冷備 √

  • InnoDB

    熱備 √

    溫備 √

    冷備 √

2)物理與邏輯

按照備份的內容分,可以分為兩種

  • 1、物理備份:直接將底層物理文件備份
  • 2、邏輯備份:通過特定的工具從數據庫中導出sql語句或者數據,可能會丟失數據精度

3)全量、差異、增量

按照每次備份的數據量,可以分為

  • 全量備份/完全備份(Full Backup):備份整個數據集( 即整個數據庫 )
  • 部分備份:備份部分數據集(例如: 只備份一個表的變化)

而部分備份又分為:差異備份和增量備份兩種

# 1、差異備份(Differential Backup)
每次備份時,都是基於第一次完全備份的內容,只備份有差異的數據(新增的、修改的、刪除的),例如

第一次備份:完全備份
第二次備份:以當前時間節點的數據為基礎,備份與第一次備份內容的差異
第三次備份:以當前時間節點的數據為基礎,備份與第一次備份內容的差異
第四次備份:以當前時間節點的數據為基礎,備份與第一次備份內容的差異
第五次備份:以當前時間節點的數據為基礎,備份與第一次備份內容的差異
。。。

# 2、增量備份(Incremental Backup )
每次備份時,都是基於上一次備份的內容(注意是上一次,而不是第一次),只備份有差異的數據(新增的、修改的、刪除的),所以增量備份的結果是一條鏈,例如

第一次備份:完全備份
第二次備份:以當前時間節點的數據為基礎,備份與第一次備份內容的差異
第三次備份:以當前時間節點的數據為基礎,備份與第二次備份內容的差異
第四次備份:以當前時間節點的數據為基礎,備份與第三次備份內容的差異
第五次備份:以當前時間節點的數據為基礎,備份與第四次備份內容的差異
。。。

針對上述三種備份方案,如何恢復數據呢

# 1、全量備份的數據恢復
只需找出指定時間點的那一個備份文件即可,即只需要找到一個文件即可

# 2、差異備份的數據恢復
需要先恢復第一次備份的結果,然后再恢復最近一次差異備份的結果,即需要找到兩個文件

# 3、增量備份的數據恢復
需要先恢復第一次備份的結果,然后再依次恢復每次增量備份,直到恢復到當前位置,即需要找到一條備份鏈

綜上,對比三種備份方案
1、占用空間:全量 > 差異 > 增量
2、恢復數據過程的復雜程度:增量 > 差異 > 全量

1.4 備份的工具

備份工具 備份速度 恢復速度 便捷性 適用存儲引擎 支持的備份類型 功能 應用場景
cp、tar等(物理) 一般 所有 冷備、全量、差異、增量 很弱 少量數據備份
lvm2快照(物理) 一般 所有 支持幾乎熱備(即差不多是熱備,哈哈),是借助文件系統管理工具進行的備份 一般 中小型數據量的備份
xtrabackup(物理) 較快 較快 是一款非常強大的熱備工具 percona提供,只支持InnoDB/XtraDB 熱備、全量、差異、增量 強大 較大規模的備份
mysqldump(邏輯) 一般 所有 支持溫備、完全備份、部分備份、對於InnoDB存儲引擎支持熱備 一般 中小型數據量的備份

此外,如果考慮到增量備份,還需要結合binlog日志(binlog只屬於增量恢復),需要用到工具mysqlbinlog,相當於邏輯備份的一種

二 設計備份策略

2.1 備份策略設計的參考值

備份數據的策略要根據不同的應用場景進行定制, 大致有幾個參考數值, 我們可以根據這些數值從而定制符合特定環境中的數據備份策略

  • 能夠容忍丟失多少數據
  • 恢復數據需要多長時間
  • 需要恢復哪一些數據

2.2 三種備份策略及應用場景

針對不同的場景下, 我們應該制定不同的備份策略對數據庫進行備份, 一般情況下, 備份策略一般為以下三種

  • 直接cp,tar復制數據庫文件
  • mysqldump+復制BIN LOGS
  • lvm2快照+復制BIN LOGS
  • xtrabackup+復制BIN LOGS**

以上的幾種解決方案分別針對於不同的場景

  1. 如果數據量較小, 可以使用第一種方式, 直接復制數據庫文件
  2. 如果數據量還行, 可以使用第二種方式, 先使用mysqldump對數據庫進行完全備份, 然后定期備份BINARY LOG達到增量備份的效果
  3. 如果數據量一般, 而又不過分影響業務運行, 可以使用第三種方式, 使用lvm2的快照對數據文件進行備份, 而后定期備份BINARY LOG達到增量備份的效果
  4. 如果數據量很大, 而又不過分影響業務運行, 可以使用第四種方式, 使用xtrabackup進行完全備份后, 定期使用xtrabackup進行增量備份或差異備份

三 備份實戰

3.1 使用cp進行備份

備份步驟

#1、向所有表施加讀鎖
FLUSH TABLES WITH READ LOCK;  

#2、備份數據文件
mkdir /jason_bak
cp -a /var/lib/mysql/* /jason_bak

模擬數據丟失並恢復

# 數據丟失
rm -rf /var/lib/mysql/*

# 恢復數據
cp -a /jason_bak/* /var/lib/mysql

# 重啟服務
systemctl restart mysql

3.2 使用mysqldump+復制BINARY LOGS備份

mysqldump命令

#==========語法
mysqldump  -h 服務器  -u用戶名  -p密碼  選項與參數 > 備份文件.sql

===選項與參數
1、-A/--all-databases	         所有庫
2、-B/--databases bbs db1 db2	 多個數據庫
3、db1                          數據庫名
4、db1 t1 t2		            db1數據庫的表t1、t2
5、-F                           備份的同時刷新binlog
6、-R 備份存儲過程和函數數據(如果開發寫了函數和存儲過程,就備,沒寫就不備)
7、--triggers 備份觸發器數據(現在都是開發寫觸發器)
8、-E/--events 備份事件調度器
9、-d 僅表結構
10、-t 僅數據
11、--master-data=1  備份文件中 change master語句是沒有注釋的,默認為1
用於已經制作好了主從,現在想擴展一個從庫的時候使用
如此備份,擴展添加從庫時導入備份文件后
便不需要再加mater_pos了
change matser to
master_host='10.0.0.111'
master_user='rep'
master_password=123
master_log_pos=120
master_log_file='master-bin.000001'
                             
12、--master-data=2  備份文件中 change master語句是被注釋的 
 
13、--lock-all-tables 備份過程中所有表從頭鎖到尾,簡單粗暴
在mysqldump導出的整個過程中以read方式鎖住數據庫中所有表,類似 flush tables with read lock 的全局鎖),
這是一個全局讀鎖,只允許讀不允許寫,以此保證數據一致性。
比如當前數據庫有如下schema:
information_schema(不會導出)
mysql
performance_schema(不會導出)
sys(不會導出)
test
test1
test2
那么我們在使用mysqldump導出時:
mysqldump --lock-all-tables --set-gtid-purged=on -AER > test.sql
指定--lock-all-tables參數,那么從一開始就對整個mysql實例加global read lock鎖。
這整個全局讀鎖會一直持續到導出結束。
所以在這個過程中,數據庫實際嚴格處於read only狀態。
所以導出的數據庫在數據一致性上是被嚴格保證的,也就是數據是一致性的。
由於這個參數會將數據庫置於read only狀態(也相當於不可使用狀態),所以默認不加該參數。
這相當於脫機備份的感覺,所以生產數據庫的備份策略上,也很少使用該參數。

該參數本身默認off,但使用該參數的話,也會自動將 --single-transaction 及 --lock-tables 參數置於 off 狀態,他們是互斥的。

對於支持事務的表例如InnoDB和BDB,推薦使用--single-transaction選項,因為它根本不需要鎖定表

14、--single-transaction: 快照備份 (搭配--master-data可以做到熱備)
保證各個表具有數據一致性快照。
指定 --single-transaction 參數,那么導出過程中只能保證每個表的數據一致性(利用多版本特性實現,目前只能針對InnoDB事務表)。
比如有一個大表,mysqldump對該表的導出需要1分鍾,那么在這1分鍾的過程中,該表時可以被正常訪問的。
(正常訪問包括增刪改查,但是alter table等對表結構發生更改的語句要被掛起。)
mysqldump能夠保證從開始對該表進行導出,一直到對該表的導出結束,該表的數據都是開始的一致性數據快照狀態。
所以該參數明顯不能保證各個表之間的數據一致性(特別是外鍵約束的父表和子表之間)。
但是該參數能夠讓數據庫處於可使用(就是應用感覺數據庫可用)狀態,相當於聯機備份,所以被經常使用。
該參數默認off。

15、--lock-tables:如果是備份所有庫,那么備份到某個庫時只鎖某個庫,其他庫可寫,而--lock-all-tables是從始自終都全都鎖定

保證各個schema具有數據一致性快照。
指定 --lock-tables 參數,那么在導出過程中能夠保證各個schema的數據一致性。
比如導出 cms 庫(該庫有155張表)時:
mysqldump --lock-tables --set-gtid-purged=off -ER -B cms>test.sql
從命令開始,就對 cms 庫的155張表加類似 lock table xxx read 的讀鎖。
這會導致在導出整個cms庫的過程中,cms庫實際上整體處於read only狀態。
但是如果我們指定如下命令:
mysqldump --lock-tables --set-gtid-purged=on -AER >test.sql
來導出全部mysql庫,那么當導出cms庫的過程中,其他 schema 實際上是可以被正常訪問的。
這個正常訪問就是可以接受所有合法的sql語句。
所以該參數只能保證各個schema自己的數據一致性快照。
該參數默認on。

#==========完整語句 
mysqldump -uroot -pEgon@123 -A -E -R --triggers --master-data=2 --single-transaction > /backup/full.sql

#====文件太大時可以壓縮 gzip ,但是gzip不屬於mysql獨有的命令,可以利用管道
mysqldump -uroot -pEgon@123 -A -E -R --triggers --master-data=2 --single-transaction | gzip > /tmp/full$(date +%F).sql.gz

#====導出時壓縮了,導入時需要解壓,可以使用zcat命令,很方便
zcat /backup/full$(date +%F).sql.gz | mysql -uroot -p123

儲備知識:binlog內容很多,如何定位到某個固定的點

===> 1、grep過濾

===> 2、檢查事件:依據End_log_pos的提示,來確定某一個事件的起始位置與結束位置
mysql> show binlog events in 'mybinlog.000001'; 
如果事件很多,可以分段查看
mysql> show binlog events in 'mybinlog.000001' limit 0,30; 
mysql> show binlog events in 'mybinlog.000001' limit 30,30; 
mysql> show binlog events in 'mybinlog.000001' limit 60,30; 

===> 3、利用mysqlbinlog命令
生產中很多庫,只有一個庫的表被刪除,我不可能把所有的庫都導出來篩選,因為那樣子binlog內容很多,辨別復雜度高,我們可以利用

[root@jason mysql]# mysqlbinlog -d db1 --start-position=123 --stop-position=154 mybinlog.000001 --base64-output=decode-rows -vvv | grep -v 'SET'

參數解釋:
1)-d 參數接庫名
mysqlbinlog -d database --base64-output=decode-rows -vvv mysql-bin.000002
2)--base64-output  顯示模式
3)-vvv			顯示詳細信息

備份:

# 1、先打開binlog日志
vim /etc/my.cnf
[mysqld]
server_id=1
log-bin=/var/lib/mysql/mybinlog
binlog_format='row' #(row,statement,mixed)    
binlog_rows_query_log_events=on
max_binlog_size=100M

# 2、登錄數據庫,插入測試數據
mysql> create database db3;
mysql> use db3;
mysql> create table t1(id int);
mysql> insert t1 values(1),(2),(3);

# 3、在命令行執行命令,進行全量備份
[root@jason mysql]# mysqldump -uroot -pEgon@123 -A -R --triggers --master-data=2 --single-transaction | gzip > /tmp/full.sql.gz

# 4、在命令行執行命令,刷新binlog,便於日后查找
[root@jason mysql]# mysql -uroot -pEgon@123 -e "flush logs"

# 5、登錄數據庫,再插入一些數據,模擬增量,這些數據寫入了新的binlog
mysql> use db3;
mysql> insert t1 values(4),(5),(6);

模擬數據損壞恢復

# 模擬數據丟失
mysql> drop database db1;

# 恢復數據
# 1、mysql數據導入時,臨時關閉binlog,不要將恢復數據的寫操作也記入
mysql> set sql_log_bin=0;

# 2、先恢復全量
mysql> source /tmp/full.sql

如果是壓縮包呢,那就這么做
mysql> system zcat /tmp/full.sql.gz | mysql -uroot -pEgon@123

# 3、再恢復增量
導出:注意導出binlog時不要加選項--base64-output
[root@jason mysql]# mysqlbinlog mybinlog.000002 --stop-position=531 > /tmp/last_bin.log
導入
mysql> source /tmp/last_bin.log

# 4、開啟二進制日志
mysql> SET sql_log_bin=ON; 

測試在線熱備份

可以先准備一個存儲過程,一直保持寫入操作,然后驗證熱備

#1. 准備庫與表
create database if not exists db1;
use db1;
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);

#2. 創建存儲過程,每隔3秒插入一條
delimiter $$ #聲明存儲過程的結束符號為$$
create procedure auto_insert1()
BEGIN
    declare i int default 1;
    while(i<3000000)do
        insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
	    select concat('jason',i,'_ok') as name,sleep(3);
		set i=i+1;
    end while;
END$$ #$$結束
delimiter ;

#3. 查看存儲過程
show create procedure auto_insert1\G 

備份:

# 1、先打開binlog日志
略

# 2、登錄數據庫,執行存儲過程
mysql> use db1;
mysql> call auto_insert1();

若想殺死存儲過程
mysql> show processlist; -- 查出id
mysql> kill id號;


# 3、在命令行執行下述命令,進行全量備份
[root@jason mysql]# mysqldump -uroot -pEgon@123 -A -R --triggers --master-data=2 --single-transaction | gzip > /tmp/full.sql.gz

# 4、全量備份完畢后的一段時間里,數據依然插入,寫入了mybinlog.000001中
#    然后我們在命令行刷新binlog,產生了新的mybinlog.000002
[root@jason mysql]# mysql -uroot -pEgon@123 -e "flush logs"

# 5、此時數據依然在插入,但都寫入了最新的mybinlog.000002中,所以需要知道的是,增量的數據在mysqlbinlog.000001與mybinlog.000002中都有
我們登錄數據庫,殺掉存儲過程,觀察到最新的數據插到了id=55的行
mysql> show processlist; -- 查出id
mysql> kill id號;

刪除數據

drop database db1;

恢復數據

# 登錄數據庫,先恢復全量
mysql> set sql_log_bin=0;
mysql> system zcat /tmp/full.sql.gz | mysql -uroot -pEgon@123
mysql> select * from db1.s1; -- 查看恢復到了id=28,剩下的去增量里恢復

# 在命令行導出mybinlog.000001中的增量,然后登錄庫進行恢復
查找位置,發現@1=29即第一列等於29,即id=29的下一個position是10275
mysql> show binlog events in 'mybinlog.000001';  
[root@jason mysql]# mysqlbinlog mybinlog.000001 --start-position=10038  --stop-position=11340 --base64-output=decode-rows -vvv | grep -v 'SET' | less

在命令行中執行導出
[root@jason mysql]# mysqlbinlog mybinlog.000001 --start-position=10275 > /tmp/1.sql

在庫內執行導入,發現恢復到了39
mysql> source /tmp/1.sql  -- 最好是在庫內恢復,因為sql_log_bin=0,導入操作不會記錄
mysql> select * from db1.s1;

# 在命令行導出mybinlog.000002中的增量,然后登錄庫進行恢復
上面恢復到了id=39,我們接着找id=40的進行恢復,查找位置
發現@1=40的position是432
發現@1=55的position是6464
mysql> show binlog events in 'mybinlog.000002'; 
[root@jason mysql]# mysqlbinlog --base64-output=decode-rows -vvv mybinlog.000002|grep -v 'SET'|grep -C20 -w '@1=40'
[root@jason mysql]# mysqlbinlog --base64-output=decode-rows -vvv mybinlog.000002|grep -v 'SET'|grep -C20 -w '@1=55'


導出
[root@jason mysql]# mysqlbinlog mybinlog.000002 --start-position=432 --stop-position=6464> /tmp/2.sql

在庫內執行導入,發現恢復到了55
mysql> source /tmp/2.sql  
mysql> select * from db1.s1;

# 開啟binlog
mysql> SET sql_log_bin=ON; 

問題:能否利用binlog做全量恢復

可以,但直接使用binlog做全量恢復,成本很高,我們只用起來做增量恢復。

正確的方案是:全備+binlog增量
每天或者每周全備一次,全備之后,那個位置點之前的binlog全都可以刪除,不可能一年有上百個binlog的庫都導出來篩選,因為那樣子binlog內容很多,辨別復雜度高,我們可以利用

3.3 使用lvm2快照備份數據

部署lvm環境

# 1、添加硬盤; 這里我們直接實現SCSI硬盤的熱插拔, 首先在虛擬機中添加一塊硬盤, 無需重啟
echo '- - -' > /sys/class/scsi_host/host0/scan 
echo '- - -' > /sys/class/scsi_host/host1/scan 
echo '- - -' > /sys/class/scsi_host/host2/scan 

# 2、創建邏輯卷
pvcreate /dev/sdb
vgcreate vg1 /dev/sdb
lvcreate -n lv1 -L 5G vg1 

# 3、格式化制作文件系統並掛載
mkfs.xfs /dev/mapper/vg1-lv1  
mkdir /lv1
mount /dev/mapper/vg1-lv1 /var/lib/mysql
chown -R mysql.mysql /var/lib/mysql

# 4、修改mysql配置文件的datadir如下
[root@node1 ~]# rm -rf /var/lib/mysql/*  # 刪除原數據
[root@node1 ~]# vim /etc/my.cnf    
[mysqld]
datadir=/var/lib/mysql

# 5、重啟MySQL、完成初始化
[root@node1 ~]# systemctl restart mysqld 

# 6、往數據庫內插入測試數據
create database db3;
use db3;
create table t1(id int);
insert t1 values(1),(2),(3);

創建快照卷並備份

mysql> FLUSH TABLES WITH READ LOCK;     #鎖定所有表
Query OK, 0 rows affected (0.00 sec)

[root@node1 lvm_data]# lvcreate -L 1G -s -n lv1_from_vg1_snap /dev/vg1/lv1   #創建快照卷

mysql> UNLOCK TABLES;  #解鎖所有表
Query OK, 0 rows affected (0.00 sec)

[root@node1 lvm_data]# mkdir /snap1  #創建文件夾
[root@node1 lvm_data]# mount -o nouuid /dev/vg1/lv1_from_vg1_snap /snap1

[root@localhost snap1]# cd /snap1/

[root@localhost snap1]# tar cf /tmp/mysqlback.tar *  

[root@localhost snap1]# umount /snap1/ -l
[root@localhost snap1]# lvremove vg1/lv1_from_vg1_snap

恢復數據

rm -rf /var/lib/mysql/*

# 恢復
tar xf /tmp/mysqlback.tar -C /var/lib/mysql/

3.4 物理備份之Xtrabackup

(1)介紹

Xtrabackup是由percona提供的mysql數據庫備份工具,據官方介紹,這也是世界上惟一一款開源的能夠對innodb和xtradb數據庫進行熱備的工具。特點:

  1. 備份過程快速、可靠;
  2. 備份過程不會打斷正在執行的事務;
  3. 能夠基於壓縮等功能節約磁盤空間和流量;
  4. 自動實現備份檢驗;
  5. 還原速度快;

使用xtrabackup使用InnoDB能夠發揮其最大功效, 並且InnoDB的每一張表必須使用單獨的表空間, 我們需要在配置文件中添加 innodb_file_per_table = ON 來開啟

(2)安裝

版本選擇

mysql 5.7以下版本,可以采用percona xtrabackup 2.4版本

mysql 8.0以上版本,可以采用percona xtrabackup 8.0版本,xtrabackup8.0也只支持mysql8.0以上的版本

比如,接觸過一些金融行業,mysql版本還是多采用mysql 5.7,當然oracle官方對於mysql 8.0的開發支持力度日益加大,新功能新特性迭代不止。生產環境采用mysql 8.0的版本比例會日益增加。


安裝方式一

# 安裝yum倉庫
yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm -y

# 安裝XtraBackup命令
yum install percona-xtrabackup-24 -y

安裝方式二

#下載epel源
wget -O /etc/yum.repos.d/epel.repo  https://mirrors.aliyun.com/repo/epel-7.repo

#安裝依賴
yum -y install perl perl-devel libaio libaio-devel perl-Time-HiRes perl-DBD-MySQL

#下載Xtrabackup
wget https://downloads.percona.com/downloads/Percona-XtraBackup-2.4/Percona-XtraBackup-2.4.23/binary/redhat/7/x86_64/percona-xtrabackup-24-2.4.23-1.el7.x86_64.rpm

# 安裝
yum localinstall -y percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpm

安裝完后會生成命令

xtrabackup      以前使用該命令
innobackupex    現在使用該命令

innobackupex是xtrabackup的前端配置工具,使用innobackupex備份時, 會調用xtrabackup備份所有的InnoDB表, 復制所有關於表結構定義的相關文件(.frm)、以及MyISAM、MERGE、CSV和ARCHIVE表的相關文件, 同時還會備份觸發器和數據庫配置文件信息相關的文件, 這些文件會被保存至一個以時間命名的目錄.

(3)Xtrabackup 備份方式(物理備份)

1.對於非innodb表(比如myisam)是直接鎖表cp數據文件,屬於一種溫備。

2.對於innodb的表(支持事務),不鎖表,cp數據頁最終以數據文件方式保存下來,並且把redo和undo一並備走,屬於熱備方式。

3.備份時讀取配置文件/etc/my.cnf

(4)Xtrabackup全量備份

#1、創建備份目錄,會把mysql的datadir中的內容備份到改目錄中
mkdir /backup

#2、全備
#2.1 在本地執行下述命令,輸入登錄數據的本地賬號與密碼
#2.2 指定備份目錄為/backup下的full目錄
innobackupex --user=root --password=123 /backup/full

#3、查看:默認會在備份目錄下生成一個以時間戳命名的文件夾
[root@localhost ~]# cd /backup/full/
[root@localhost full]# ls
2021-07-16_16-09-47
[root@localhost full]# ls 2021-07-16_16-09-47/ #備份目錄
。。。
[root@localhost full]# ls /var/lib/mysql # 數據目錄
。。。

# 4、去掉時間戳,讓備份數據直接放在備份目錄下
我們在寫備份腳本和恢復腳本,恢復的時候必須指定上一次備份的目錄,如果備份目錄帶着時間戳,該時間戳我們很難在腳本中確定,無為了讓腳本編寫更加方便,我們可以使用選項--no-timestamp去掉時間戳,讓備份內容直接放置於我們指定的目錄下(ps:金融公司喜歡每天全備,每小時增備,如果備份目錄帶着時間戳,看似合理,但確實會很讓頭疼)
[root@localhost full]# rm -rf 2021-07-16_17-45-53/
[root@localhost full]# innobackupex --user=root --password=123 --no-timestamp /backup/full

# 補充:關於備份目錄下新增的文件說明,可用cat命令查看
xtrabackup_checkpoints 存儲系統版本號,增備的時候會用到
xtrabackup_info 存儲UUID,數據庫是由自己的UUID的,如果相同,做主從會有問題
xtrabackup_logfile 就是redo

(5)Xtrabackup增量備份

#一 基於上一次備份進行增量,參數說明:
--incremental:開啟增量備份功能
--incremental-basedir:上一次備份的路徑
	
#二 加上上一次命令
innobackupex --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/full/ /backup/xtra
	
#三 判斷數據備份是否銜接
cat /backup/full/xtrabackup_checkpoints
    backup_type = full-backuped
    from_lsn = 0
    to_lsn = 1808756
    last_lsn = 1808756
    compact = 0
    recover_binlog_info = 0
    flushed_lsn = 1808756
	
cat /backup/xtra/xtrabackup_checkpoints 
    backup_type = incremental
    from_lsn = 1808756  # 值應該與全被的to_lsn一致
    to_lsn = 1808756
    last_lsn = 1808756
    compact = 0
    recover_binlog_info = 0
    flushed_lsn = 1808756

更多參數詳見:https://www.cnblogs.com/linhaifeng/articles/15021166.html

(6)企業實戰:Xtrabackup + Binlog恢復

mysql配置文件:數據目錄與binlog放在不同的文件夾下

[mysqld]
datadir=/var/lib/mysql
default-storage-engine=innodb
innodb_file_per_table=1
server_id=1
log-bin=/data/binlog/mybinlog  
binlog_format='row' #(row,statement,mixed)    
binlog_rows_query_log_events=on
max_binlog_size=100M

為binlog日志創建目錄

mkdir -p /data/binlog/
chown -R mysql.mysql /data/

啟動mysql

systemctl restart mysql

模擬數據

create database full charset utf8mb4;
use full;
create table t1 (id int);
insert into t1 values(1),(2),(3);
commit;

進行周日的全備

# 1、事先創建好備份目錄
[root@db01 backup]# rm -rf /backup 
[root@db01 backup]# mkdir /backup   

# 2、全備
[root@db01 backup]# innobackupex --user=root --password=123 --no-timestamp  --parallel=5  /backup/full

模擬周一的數據變化

create database inc1 charset utf8mb4;
use inc1;
create table t1 (id int);
insert into t1 values(1),(2),(3);
commit;

進行周一的增量備份

innobackupex  --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/full /backup/inc1 

檢查本次備份的LSN

[root@localhost backup]# cat /backup/full/xtrabackup_checkpoints 
backup_type = full-backuped
from_lsn = 0
to_lsn = 1817002
last_lsn = 1817002
compact = 0
recover_binlog_info = 0
flushed_lsn = 1817002
[root@localhost backup]# cat /backup/inc1/xtrabackup_checkpoints 
backup_type = incremental
from_lsn = 1817002
to_lsn = 1825905
last_lsn = 1825905
compact = 0
recover_binlog_info = 0
flushed_lsn = 1825905
[root@localhost backup]# 

模擬周二數據變化

create database inc2 charset utf8mb4;
use inc2;
create table t1 (id int);
insert into t1 values(1),(2),(3);
commit;

周二的增量

innobackupex   --user=root --password=123 --no-timestamp --incremental --incremental-basedir=/backup/inc1 /backup/inc2 

周三的數據變化

create database inc3 charset utf8mb4;
use inc3;
create table t1 (id int);
insert into t1 values(1),(2),(3);
commit;

模擬上午10點數據庫崩潰

systemctl stop mysql # pkill -9 mysqld
\rm -rf /var/lib/mysql/*

恢復思路

1. 停業務,掛維護頁
2. 查找可用備份並處理備份:full+inc1+inc2 
3. 找到binlog中: inc2 到 故障時間點的binlog
4. 恢復全備+增量+binlog
5. 驗證數據
6. 起業務,撤維護頁

恢復前的准備
[rml_read_more]:

所有增量必須要按順序合並到全備當中才能用於恢復

#(1) 整理full,--use-memory越大效率越高,但是不要超過內存大小,超過則報錯
innobackupex --apply-log --use-memory=3G --redo-only  /backup/full

--apply-log:該選項表示同xtrabackup的--prepare參數,一般情況下,在備份完成后,數據尚且不能用於恢復操作,因為備份的數據中可能會包含尚未提交的事務或已經提交但尚未同步至數據文件中的事務。因此,此時數據 文件仍處理不一致狀態。--apply-log的作用是通過回滾未提交的事務及同步已經提交的事務至數據文件使數據文件處於一致性狀態。

#(2) 合並inc1到full,並整理備份
innobackupex --apply-log --use-memory=3G --redo-only  --incremental-dir=/backup/inc1 /backup/full 

#(3) 合並后對比inc1與full的LSN號:last_lsn保持一致
cat /backup/full/xtrabackup_checkpoints 
cat /backup/inc1/xtrabackup_checkpoints 

#(4) 合並inc2到full,並整理備份 (合並最后一個增量備份時不要加--redo-only)
innobackupex --apply-log --use-memory=3G --incremental-dir=/backup/inc2 /backup/full 

#(5) 合並后對比inc2與full的LSN號:last_lsn保持一致
cat /backup/full/xtrabackup_checkpoints 
cat /backup/inc2/xtrabackup_checkpoints 

#(6) 最后一次整理ful
innobackupex --use-memory=3G --apply-log  /backup/full

#(7) 截取二進制日志
# 起點
cat /backup/inc2/xtrabackup_binlog_info
輸出內容如下
mysql-bin.000031    1997    aa648280-a6a6-11e9-949f-000c294a1b3b:1-17,
e16db3fd-a6e8-11e9-aee9-000c294a1b3b:1-9

# 終點:
mysqlbinlog /data/binlog/mysql-bin.000031 |grep 'SET'

SET @@SESSION.GTID_NEXT= 'e16db3fd-a6e8-11e9-aee9-000c294a1b3b:12'/*!*/;

# 導出:
mysqlbinlog --skip-gtids --include-gtids='e16db3fd-a6e8-11e9-aee9-000c294a1b3b:10-12' /data/binlog/mysql-bin.000031>/backup/binlog.sql

或:
mysqlbinlog /data/binlog/mybinlog.000003  --start-position=1648 > /backup/binlog.sql恢復備份的數據
cp -a  /backup/full/* /var/lib/mysql
chown -R mysql.mysql /var/lib/mysql
systemctl start mysql
mysql -uroot -p123
> set sql_log_bin=0;
> source /backup/binlog.sql

驗證

select * from full.t1;
select * from inc1.t1;
select * from inc2.t1;
select * from inc3.t1;

恢復

四 自動備份腳本

4.1 備份計划

1. 什么時間  2:00
2. 對哪些數據庫備份
3. 備份文件放的位置

4.2 備份腳本

備份腳本:
[root@jason ~]# vim /mysql_back.sh
#!/bin/bash
back_dir=/backup
back_file=`date +%F`_all.sql
user=root
pass=123

if [ ! -d /backup ];then
        mkdir -p /backup
fi

# 備份並截斷日志
mysqldump -u${user} -p${pass} --events -R --triggers --master-data=2 --single-transaction --all-databases > ${back_dir}/${back_file}

mysql -u${user} -p${pass} -e 'flush logs'

# 只保留最近一周的備份
cd $back_dir
find . -mtime +7 -exec rm -rf {} \;

4.3 手動測試

手動測試:
chmod a+x /mysql_back.sh
chattr +i /mysql_back.sh
bash /mysql_back.sh

4.4 配置計划任務

配置cron:
[root@jason ~]# crontab -l
0 2 * * * /mysql_back.sh

五 企業案例

5.1 背景

1.正在運行的網站系統,MySQL數據庫,數據量25G,日業務增量10-15M。

2.備份策略:每天23:00,計划任務調用mysqldump執行全備腳本

3.故障時間點:上午10點開發人員誤刪除一個核心業務表,如何恢復?

5.2 處理故障思路

1.停業務避免數據的二次傷害
2.找一個臨時的庫,恢復前一天的全備
3.截取前一天23:00到第二天10點誤刪除之間的binlog,恢復到臨時庫
4.測試可用性和完整性
5.開啟業務前的兩種方式
	1)直接使用臨時庫頂替原生產庫,前端應用割接到新
	2)將誤刪除的表單獨導出,然后導入到原生產環境
6.對外開放業務

5.3 故障模擬

1)准備初始數據

#刷新binlog使內容更清晰
flush logs;
#查看當前使用的binlog
show master status;

#准備測試庫與數據
create database dbtest;
use dbtest;
create table t1(id int,name varchar(16));
insert t1 values
(1,"jason1"),
(2,"jason2"),
(3,"jason3");

create table t2 select * from t1;

2)全備

[root@db01 ~]# mkdir /backup
[root@db01 ~]# mysqldump -uroot -pEgon@123 -A -R --triggers --master-data=2 --single-transaction |gzip > /backup/full.sql.gz  # 通常備份文件應該帶時間,此處略

3)模擬23:00到10:00的操作

use dbtest;
create table t3 select * from t1;
select * from t3;
update t1 set name="EGON" where id=2;
delete from t2 where id>2;

4)模擬10:00刪庫操作

#刪庫、跑路
drop database dbtest;

5.4 恢復數據

1)先停生產庫,避免數據二次傷害

[root@db01 ~]# systemctl stop mysql

2)准備新庫,在新庫中完成數據恢復操作后再更新給生成庫

3)通過binlog找到23:00到第二天10:00之間新增的數據

#1.找到結束位置點:
mysql> show master status;
mysql> show binlog events in 'mybinlog.000002';
[root@localhost backup]# cd /var/lib/mysql
[root@localhost mysql]# mysqlbinlog --base64-output=decode-rows -vvv --start-datetime="2021-07-16 02:00:00" mybinlog.000002 

3.取出位置點之間新增的數據
[root@db01 ~]# mysqlbinlog --start-position=694 --stop-position=1249 mybinlog.000002 > /backup/xin.sql 

4)將前一天的全備數據和新增的數據拷貝到新數據庫

scp /backup/full.sql.gz  172.16.1.52:/tmp/  
scp /backup/xin.sql  172.16.1.52:/tmp/

5)將前一天的全備與增量恢復到新庫

mysql> set sql_log_bin=0;
mysql> system zcat /tmp/full.sql.gz | mysql -uroot -pEgon@123

mysql> source /tmp/xin.sql;

6)查看表和數據驗證數據完整

mysql> use dbtest;
mysql> show tables;
mysql> select * from t1;
mysql> select * from t2;
mysql> select * from t3;

7)恢復生產環境提供服務

1.將恢復的表導出,導入到生產庫(如果核心業務表很小)
	1)導出指定表
		[root@db02 mysql]# mysqldump dbtest t1 t2 t3 > /tmp/test.sql
	2)將sql傳輸到生產庫
		[root@db02 mysql]# scp /tmp/test.sql 172.16.1.51:/tmp/
	3)指定庫導入表
		[root@db01 data]# mysql backup < /tmp/test.sql

2.應用服務修改數據庫配置連接到新庫(如果核心業務表很大)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM