1. 程式人生 > 實用技巧 >MySQL資料備份及恢復

MySQL資料備份及恢復

MySQL備份一般採用全庫備份加日誌備份的方式,根據業務的需要,可以採用每週日凌晨1點進行完全備份以及每小時進行一次增量備份,這樣在MySQL故障後可以使用完全備份和日誌備份儘可能的去恢復最完整的資料。

一、binlog日誌恢復

MySQL的二進位制日誌記錄著該資料庫所有增刪改的操作日誌(前提是需要自己開啟binlog),還包括了這些操作的執行時間,binlog的使用場景無外乎就是主從同步及恢復資料庫。開啟binlog功能,需要編輯MySQL的主配置檔案!

1.1 檢視二進位制日誌功能是否開啟

mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | OFF   |
+---------------+-------+
#OFF表示未開啟

1.2 開啟二進位制日誌功能

[root@db01 ~]# vim /etc/my.cnf
#在mysqld欄位下寫入下面配置
log-bin=/usr/local/mysql/data/bin-log
#指定二進位制日誌存放路徑及二進位制日誌字首
server_id=1
#如果要開啟bin-log功能,需指定server_id,否則可能報錯
[root@db01 ~]# systemctl restart mysqld
#開啟bin-log功能需重啟mysql服務
[root@db01 ~]# cd /usr/local/mysql/data/
[root@db01 data]# ll bin-log.*
-rw-r----- 1 mysql mysql 154 4月  16 20:35 bin-log.000001
#該檔案為二進位制日誌檔案,每次重啟mysql或執行flush logs命令,就會產生一個新的二進位制日誌檔案
-rw-r----- 1 mysql mysql  37 4月  16 20:35 bin-log.index
#二進位制日誌檔案的索引

注意:開啟二進位制日誌功能後,所有增刪改的操作都會記錄到二進位制日誌檔案當中,注意,是增刪改的操作,不包括查操作!

1.3 確認二進位制日誌功能已經開啟

mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin       | ON    |
+---------------+-------+
#ON表示已經開啟二進位制日誌功能

1.4 執行增刪改以便測試bin_log是否有記錄

mysql> reset master;
#情況所有的二進位制檔案,從000001開始
mysql>  create database test_db1;
#建立一個數據庫
mysql> use test_db1;
mysql> create table tb1(id int primary key auto_increment,name varchar(20));
#建立一個表
mysql> insert into tb1(name) values ('zhangsan');
mysql> insert into tb1(name) values ('lisi');
#插入測試資料
mysql> flush logs;
#重新開始一個新的日誌檔案再執行操作。注意,此時上面所有的操作寫入的是第一個二進位制日誌檔案
mysql> delete from tb1 where name='lisi';
#刪除第二條資料
mysql> insert into tb1(name) values ('tom');
#再插入一條新資料
#以上兩條操作是為了寫入第二個日誌檔案

1.5 MySQL中檢視二進位制日誌檔案

① 檢視二進位制日誌檔案
mysql> show binary logs;
+----------------+-----------+
| Log_name       | File_size |
+----------------+-----------+
| bin-log.000001 |      1129 |
| bin-log.000002 |       693 |
+----------------+-----------+
② 檢視二進位制日誌檔案內容

完整的命令格式:

SHOW BINLOG EVENTS[IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
# in:指定要檢視的二進位制檔案;
# from:指定從哪個“pos”位置開始檢視
# limit:限制返回的行數,offset是指跳過多少行再顯示

注:如果不指定二進位制檔名,那麼預設顯示第一個二進位制日誌檔案中的事件,檔案內容中包含了日誌檔名、事件的開始位置、事件型別、結束位置、資訊等內容。

mysql> show binlog events in 'bin-log.000001';
+----------------+------+----------------+-----------+-------------+--------------------------------------------------------------------------------------+
| Log_name       | Pos  | Event_type     | Server_id | End_log_pos | Info                                                                                 |
+----------------+------+----------------+-----------+-------------+--------------------------------------------------------------------------------------+
| bin-log.000001 |    4 | Format_desc    |         1 |         123 | Server ver: 5.7.29-log, Binlog ver: 4                                                |
| bin-log.000001 |  123 | Previous_gtids |         1 |         154 |                                                                                      |
| bin-log.000001 |  154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                                                 |
| bin-log.000001 |  219 | Query          |         1 |         325 | create database test_db1                                                             |
| bin-log.000001 |  325 | Anonymous_Gtid |         1 |         390 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                                                 |
| bin-log.000001 |  390 | Query          |         1 |         540 | use `test_db1`; create table tb1(id int primary key auto_increment,name varchar(20)) |
| bin-log.000001 |  540 | Anonymous_Gtid |         1 |         605 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                                                 |
| bin-log.000001 |  605 | Query          |         1 |         681 | BEGIN                                                                                |
| bin-log.000001 |  681 | Table_map      |         1 |         734 | table_id: 108 (test_db1.tb1)                                                         |
| bin-log.000001 |  734 | Write_rows     |         1 |         783 | table_id: 108 flags: STMT_END_F                                                      |
| bin-log.000001 |  783 | Xid            |         1 |         814 | COMMIT /* xid=13 */                                                                  |
| bin-log.000001 |  814 | Anonymous_Gtid |         1 |         879 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                                                 |
| bin-log.000001 |  879 | Query          |         1 |         955 | BEGIN                                                                                |
| bin-log.000001 |  955 | Table_map      |         1 |        1008 | table_id: 108 (test_db1.tb1)                                                         |
| bin-log.000001 | 1008 | Write_rows     |         1 |        1053 | table_id: 108 flags: STMT_END_F                                                      |
| bin-log.000001 | 1053 | Xid            |         1 |        1084 | COMMIT /* xid=14 */                                                                  |
| bin-log.000001 | 1084 | Rotate         |         1 |        1129 | bin-log.000002;pos=4                                                                 |
+----------------+------+----------------+-----------+-------------+--------------------------------------------------------------------------------------+
  • show master logs:也是檢視二進位制日誌檔案;
  • purge binary logs:用於刪除二進位制檔案;

舉例:

purge binary logs to 'mysql-bin.00010'; 
#把這個檔案之前的其他檔案都刪除掉
purge binary logs before '2016-08-28 22:46:26';
#把指定時間之前的二進位制檔案刪除了

1.6 通過二進位制日誌恢復資料

假設在開始刪除lisi記錄的那條sql語句是誤操作,現在要通過二進位制日誌來恢復資料。

① 第一步

首先需要找到刪除lisi記錄的sql語句在二進位制日誌中的位置,每條sql語句都是一個事務,所以需要從其begin到commit,才算是完整的sql語句。如下:

[root@db01 ~]# cd /usr/local/mysql/data/
[root@db01 data]# mysqlbinlog -v bin-log.000002
#檢視二進位制日誌檔案
# at 348     
#200416 22:36:13 server id 1  end_log_pos 393 CRC32 0x32b0e8ea 	Delete_rows: table id 108 flags: STMT_END_F

BINLOG '
XW2YXhMBAAAANQAAAFwBAAAAAGwAAAAAAAEACHRlc3RfZGIxAAN0YjEAAgMPAjwAAlJf/qg=
XW2YXiABAAAALQAAAIkBAAAAAGwAAAAAAAEAAgAC//wCAAAABGxpc2nq6LAy
'/*!*/;
### DELETE FROM `test_db1`.`tb1`   #找到刪除資料的SQL語句
### WHERE
###   @1=2
###   @2='lisi'
# at 393
#200416 22:36:13 server id 1  end_log_pos 424 CRC32 0x08192b48 	Xid = 16
COMMIT/*!*/;
# at 424
#可以看出,delete事件發生在position是348,事件結束是393
② 第二步

事件恢復流程:直接用bin-log日誌將資料庫恢復到刪除位置219前,然後跳過故障點,再進行恢復下面所有的操作,具體恢復流程如下:

[root@db01 data]# mysqlbinlog bin-log.000001 > /tmp/01.sql
[root@db01 data]# mysqlbinlog --stop-position=348 bin-log.000002 > /tmp/348.sql
[root@db01 data]# mysqlbinlog --start-position=393 bin-log.000002 > /tmp/393.sql
#匯出相關binlog檔案(將二進位制檔案轉換為sql語句生成新的檔案)

上述指令中,第一條比較好理解,無非就是使用msyqlbinlog檢視第一個二進位制檔案,並生成新檔案,後面兩條指令呢,--stop-postition意思是檢視時到219這個位置不檢視,一直到393才又開始接著檢視。最後的結果就是新生成的檔案中不會包含刪除lisi記錄的sql語句。

③ 第三步

模擬誤操作!

mysql> drop database test_db1;
#刪除資料庫
④ 第四步

恢復資料!

[root@db01 data]# mysql -uroot -p123 < /tmp/01.sql 
#恢復第一個日誌檔案
[root@db01 data]# mysql -uroot -p123 < /tmp/348.sql 
#恢復第二個日誌檔案
[root@db01 data]# mysql -uroot -p123 < /tmp/393.sql 
#恢復第三個日誌檔案
⑤ 第五步

驗證資料已經恢復!

mysql> select schema();
+----------+
| schema() |
+----------+
| test_db1 |
+----------+
#確認當前所在庫
mysql> select * from tb1;
+----+----------+
| id | name     |
+----+----------+
|  1 | zhangsan |
|  2 | lisi     |
|  3 | tom      |
+----+----------+
#驗證資料

二、mysqldump備份工具

mysqldump是mysql用於備份和資料轉移的一個工具。主要產生一系列的SQL語句,可以封裝到檔案,該檔案包含有所有重建資料庫所需要的 SQL命令,如CREATE DATABASECREATE TABLEINSERT等等。可以用來實現輕量級的快速遷移或恢復資料庫。 mysqldump 是將資料表導成 SQL 指令碼檔案,在不同的 MySQL 版本之間升級時相對比較合適,這也是最常用的備份方法。 mysqldump一般在資料量很小的時候(幾個G)可以用於備份。當資料量比較大的情況下,就不建議用mysqldump工具進行備份了。

mysqldump可以針對單個表、多個表、單個數據庫、多個數據庫、所有資料庫進行匯出的操作。

2.1 備份一個表

[root@db01 data]# mysqldump -uroot -p123 mysql user > /tmp/mysql-user.sql
 #備份mysql庫中的user表
[root@db01 data]# ll /tmp/mysql-user.sql 
#確認備份檔案
-rw-r--r-- 1 root root 5665 4月  16 23:06 /tmp/mysql-user.sql

2.2 恢復mysql資料庫中的user表

[root@db01 data]# mysql -uroot -p123 mysql < /tmp/mysql-user.sql 

2.3 備份mysql庫

[root@db01 data]# mysqldump -uroot -p123 --databases mysql > /tmp/mysql.sql
[root@db01 data]# ll /tmp/mysql.sql 
-rw-r--r-- 1 root root 1180261 4月  16 23:10 /tmp/mysql.sql

2.4 恢復mysql庫

[root@db01 data]# mysql -uroot -p123 < /tmp/mysql.sql 

2.5 備份所有的庫

當匯出的資料量較大時,可以新增“--opt”選項以優化執行速度!

[root@db01 data]# mysqldump -uroot -p123 --opt --all-databases > /tmp/all-data.sql
[root@db01 data]# ll /opt/all-data.sql
-rw-r--r-- 1 root root 1896852 4月  16 23:13 /tmp/all-data.sql

2.6 生產使用的備份命令

[root@db01 ~]# mysqldump -uroot -p123 -A --master-data=2 --single-transaction -R -E --triggers --max_allowed_packet=64M > /mnt/full.sql
#將資料庫中的所有內容進行備份
# --master-data=2:備份時自動記錄binlog資訊便於恢復
# --single-transaction:減少鎖表時間
# -R:備份儲存過程
# -E:備份事件
# --triggers:備份觸發器
# --max_allowed_packet=64M:要傳送或接收的最大資料包長度

三、xtrabackup概述及安裝

3.1 xtrabackup概述

Xtrabackup提供了兩種命令列工具:

  • xtrabackup:專用於備份InnoDB和XtraDB引擎的資料;
  • innobackupex:是一個perl指令碼,在執行過程中會呼叫xtrabackup命令,這個命令即可以實現備份InnoDB,也可以備份Myisam引擎的物件;

xtrabackup是由percona提供的MySQL資料庫備份工具,其備份速度快並且可靠;備份過程不會打斷正在執行的事務;能夠基於壓縮等功能節約磁碟空間和流量;自動實現備份檢驗;還原速度快。

3.2 xtrabackup安裝

[root@db01 ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
[root@db01 ~]# yum -y install perl perl-devel libaio libaio-devel perl-Time-HiRes perl-DBD-MySQL libev
[root@db01 ~]# wget https://www.percona.com/downloads/XtraBackup/Percona-XtraBackup-2.4.12/binary/redhat/7/x86_64/percona-xtrabackup-24-2.4.12-1.el7.x86_64.rpm
[root@db01 ~]# yum -y install percona-xtrabackup-24-2.4.12-1.el7.x86_64.rpm

xtrabackup中主要包含兩個工具:

  • xtrabackup:是用於熱備份innodb,xtradb表中資料的工具,支援線上熱備份,可以在不加鎖的情況下備份innodb資料表,不過該工具不能操作myisam引擎表;
  • innobackupex:是將xtrabackup進行封裝的perl指令碼,能同時處理innodb和myisam,但在處理myisam時需要加一個讀鎖,由於操作myisam時需要加讀鎖,所以會堵塞線上服務的寫操作,而Innodb沒有這樣的限制;

四、xtrabackup完全備份+binlog增量備份

4.1 開啟二進位制日誌

[root@db01 ~]# vim /etc/my.cnf
#在mysqld欄位下寫入下面配置
log-bin=/usr/local/mysql/data/bin-log
#指定二進位制日誌存放路徑及二進位制日誌字首
server_id=1
#如果要開啟bin-log功能,需指定server_id,否則可能報錯
[client]
socket=/usr/local/mysql/mysql.sock
#xtrabackup是伺服器的客戶端工具,連線資料庫時需指定sock檔案
[root@db01 ~]# systemctl restart mysqld
#開啟bin-log功能需重啟mysql服務

4.2 建立備份所需目錄

[root@db01 ~]# mkdir -p /opt/mysqlbackup/{full,inc}
#full:全備存放的目錄; inc:增量備份存放的目錄

4.3 建立備份使用者

[root@db01 ~]# mysql -uroot -p123
mysql> create user bakuser@'localhost' identified by '123';
mysql> revoke all privileges,grant option from 'bakuser'@'localhost';
mysql> grant reload,lock tables,replication client,process on *.* to bakuser@'localhost';
mysql> flush privileges;

4.4 完整備份

[root@db01 ~]# innobackupex --user=bakuser --password=123 /opt/mysqlbackup/full/
……………………
200417 11:20:45 completed OK!
#出現該資訊表示備份成功
# --user:指定連線資料庫的使用者名稱;
# --password:指定連線資料庫的密碼;
# --defaults-file:指定資料庫的配置檔案my.cnf,innobackupex要從其中獲取datadir等資訊,如果不指定,則會預設去搜索my.cnf這個檔案,搜尋順序和mysql啟動時的搜尋順序一樣;
# --database:指定要備份的資料據庫,這裡指定的資料庫只對myisam表有效,對於innodb資料來說都是全備(所有資料庫中的innodb資料都進行了備份,不是隻備份指定的資料庫,恢復時也一樣);
# /opt/mysqlbackup/full:是備份檔案的存放位置;

4.5 檢視備份後的檔案

[root@db01 ~]# ll /opt/mysqlbackup/full/2020-04-17_11-20-43/
#備份後目錄為當前時間的年月日時分秒
總用量 12340
-rw-r----- 1 root root      487 4月  17 11:20 backup-my.cnf
#備份命令用到的配置選項資訊
-rw-r----- 1 root root      314 4月  17 11:20 ib_buffer_pool
#buffer緩衝區相關的資訊
-rw-r----- 1 root root 12582912 4月  17 11:20 ibdata1
drwxr-x--- 2 root root     4096 4月  17 11:20 mysql
drwxr-x--- 2 root root     8192 4月  17 11:20 performance_schema
drwxr-x--- 2 root root     8192 4月  17 11:20 sys
drwxr-x--- 2 root root       20 4月  17 11:20 test
drwxr-x--- 2 root root       20 4月  17 11:20 test01
drwxr-x--- 2 root root       20 4月  17 11:20 test02
-rw-r----- 1 root root       20 4月  17 11:20 xtrabackup_binlog_info
#二進位制日誌的截止位置
-rw-r----- 1 root root      113 4月  17 11:20 xtrabackup_checkpoints
#儲存的是備份型別(如完全或增量)、備份狀態(如是否已經為perpared狀態(備份恢復前需要的狀態)),和LSN(日誌序列號)範圍資訊,每個innodb頁(一般為16k大小)都會包含一個日誌序列號,LSN時整個資料庫系統同的系統版本號,每個頁面相關的LSN能夠表明此頁面最近是如何發生改變的
-rw-r----- 1 root root      482 4月  17 11:20 xtrabackup_info
#備份指令相關的資訊
-rw-r----- 1 root root     2560 4月  17 11:20 xtrabackup_logfile

如果只想讓備份後的目錄為年月日的格式,可以使用以下命令:

[root@db01 ~]# innobackupex --user=root --password=123 --no-timestamp /opt/mysqlbackup/full/full_`date +%F`
[root@db01 ~]# ll /opt/mysqlbackup/full/
總用量 0
drwxr-x--- 8 root root 263 4月  17 11:20 2020-04-17_11-20-43
drwxr-x--- 8 root root 263 4月  17 11:28 full_2020-04-17
#更改後的目錄名

4.6 通過二進位制日誌進行增量備份

在進行增量備份前,需要先檢視到完全備份時binlog日誌位置(position),如下:

[root@db01 ~]# cat /opt/mysqlbackup/full/2020-04-17_11-20-43/xtrabackup_binlog_info 
bin-log.000001	1502
# 得到完全備份是備份到了bin_log.000001二進位制日誌中的1502的位置

通過二進位制日誌進行增量備份

在增量備份前,自行向資料庫中進行增刪改等操作,以便產生新的二進位制日誌。

[root@db01 ~]# mysqlbinlog --start-position=1502 /usr/local/mysql/data/bin-log.000001 > /opt/mysqlbackup/inc/`date +%F`.sql

4.7 模擬資料丟失、恢復資料

[root@db01 ~]# systemctl stop mysqld
[root@db01 ~]# rm -rf /usr/local/mysql/data/*

還原完全備份的大概流程如下:
準備(prepare)一個完全備份,一般情況下,在備份完成後,資料還不能用於恢復操作,因為備份的資料中可能包含尚未提交的事務或已經提交但尚未同步至資料檔案中的事務。因此,此時資料檔案仍處於不一致狀態,“準備”的主要作用就是通過回滾未提交的事務及同步已提交的事務至資料檔案也使得資料檔案處於一致性狀態。在準備過程結束後,Innodb表資料已經前滾到整個備份結束的點,而不是回滾到xtrabackup剛開始的點。

通過--apply-log選項可用於實現上述功能,命令如下:

[root@db01 ~]# innobackupex --apply-log /opt/mysqlbackup/full/2020-04-17_11-20-43/
……………………
200417 11:38:22 completed OK!
#恢復成功
[root@db01 ~]# cat /opt/mysqlbackup/full/2020-04-17_11-20-43/xtrabackup_checkpoints 
backup_type = full-prepared
#當準備工作完成後,備份目錄下的此檔案內容中的備份型別會為:full-prepared
from_lsn = 0
to_lsn = 2629998
last_lsn = 2630007
compact = 0
recover_binlog_info = 0

在實現“準備”的過程中,innobackupex通常還可以使用“--user-memory”選項來指定其可以使用的記憶體大小,預設為100M,如果有足夠的記憶體,可以多劃分一些記憶體給prepare的過程,以提高其完成速度。

在準備工作完成後,即可使用以下命令進行恢復:

[root@db01 ~]#  innobackupex --copy-back /opt/mysqlbackup/full/2020-04-17_11-20-43/
……………………
200417 11:41:20 completed OK!
#表示恢復成功
[root@db01 ~]# ll /usr/local/mysql/data/
總用量 122920
-rw-r----- 1 root root      314 4月  17 11:41 ib_buffer_pool
-rw-r----- 1 root root 12582912 4月  17 11:41 ibdata1
-rw-r----- 1 root root 50331648 4月  17 11:41 ib_logfile0
-rw-r----- 1 root root 50331648 4月  17 11:41 ib_logfile1
-rw-r----- 1 root root 12582912 4月  17 11:41 ibtmp1
drwxr-x--- 2 root root     4096 4月  17 11:41 mysql
drwxr-x--- 2 root root     8192 4月  17 11:41 performance_schema
drwxr-x--- 2 root root     8192 4月  17 11:41 sys
drwxr-x--- 2 root root       20 4月  17 11:41 test
drwxr-x--- 2 root root       20 4月  17 11:41 test01
drwxr-x--- 2 root root       20 4月  17 11:41 test02
-rw-r----- 1 root root      482 4月  17 11:41 xtrabackup_info
-rw-r----- 1 root root        1 4月  17 11:41 xtrabackup_master_key_id
#確認資料已經恢復,但是使用者都是root
[root@db01 ~]# chown -R mysql.mysql /usr/local/mysql/data/
#更改檔案所屬許可權
[root@db01 ~]# systemctl start mysqld
#如果丟失時,沒有關閉資料庫,那麼在資料恢復後,需要重啟伺服器,否則資料不統一

4.8 還原增量備份

為了防止還原時產生大量的二進位制日誌,在還原時最好臨時關閉二進位制日誌,如下:

[root@db01 ~]# mysql -uroot -p123 -e 'set sql_log_bin=0;'
#臨時關閉二進位制日誌
[root@db01 ~]# mysql -uroot -p123 < /opt/mysqlbackup/inc/2020-04-17.sql 
#恢復二進位制日誌
[root@db01 ~]# mysql -uroot -p123 -e 'set sql_log_bin=1;'
#開啟二進位制日誌
[root@db01 ~]# mysql -uroot -p123 -e 'select * from test.t1;'
+------+
| id   |
+------+
|    1 |
|    2 |
|    3 |
+------+
#確認資料已經被恢復