(7) MySQL數據庫備份詳解


對於任何數據庫來說,備份都是非常重要的

數據庫復制不能取代備份的作用

比如我們由於誤操作,在主數據庫上刪除了一些數據,由於主從復制的時間很短,在發現時,從數據庫上的數據可能也已經被刪除了,
我們不能使用從數據庫上的數據來恢復主數據庫上的數據,只能通過備份進行誤刪除數據的恢復

一. 備份的分類

1.按備份的結果來分:

邏輯備份

其備份結果為SQL語句,適合於所有存儲引擎,恢復時需要較多時間,邏輯備份時,對於MyISAM存儲引擎是需要進行鎖表操作的,通過使用的mysqldump就是一種邏輯備份工具

物理備份

是對數據庫目錄的拷貝,其備份結果的大小通常也與備份數據的數據目錄大小相同,物理備份和恢復通常比邏輯備份要快,因為只需要對mysql數據目錄拷貝即可,也正是因為這點,對於內存表只能備份其結構,無法備份數據(因為其數據存儲在內存中,沒有實際的物理數據文件)

物理備份的方式

進行物理備份,我們可以采用離線備份和在線備份的方式進行備份

 離線備份:需要對數據庫進行停機,或對整個數據庫進行鎖定的情況下進行
 在線備份:需要使用第三方工具,如  XtraBackup

2. 按備份的數據庫的內容來分

2.1 全量備份

是對整個數據庫的一個完整備份

2.2 增量備份

是在上次全量或增量備份的基礎上,對更改過的數據進行的備份

注意

Mysql官方提供的mysqldump命令並不支持增量備份

通常情況要使用mysqldump來進行增量備份的話,增量備份只能通過備份Mysql的二進制日志來實現

XtraBackup本身就提供了增量備份的功能,所以對於Innodb來說,使用XtraBackup進行備份更加安全高效

無論是使用XtraBackup還是Mysqldump 進行備份,要進行基於時間點的恢復時都需要利用Mysql的二進制日志,所以通常情況下我們需要對Mysql的二進制日志也進行備份

二. 使用mysqldump進行備份

mysqldump 是mysql官方提供的邏輯備份工具,其備份結果是可讀的SQL文件

mysqldump 支持多種語法

1. 常用語法

對一個數據庫下的一個或多個表進行備份

mysqldump [OPTIONS] database [tables]

備份多個表時,table直接用空格進行分隔

對指定的多個數據庫進行備份

mysqldump [OPTIONS] --database [OPTIONS] DB1 [DB2..]

對整個mysql實例下的所有數據庫進行備份

mysqldump [OPTIONS] --all-database [OPTIONS]

2. 常用參數

-u,--user = name    #  指定備份時所使用的數據庫賬號
-p. --password [=name]    # 指定賬號的密碼
--single-transaction  # 使用此參數會在備份前先執行start transaction命令啟動一個事務,以此來獲得數據庫備份時的數據的一致性,由於是通過事務保證數據的一致性,所以此參數只對Innodb存儲引擎有效;當使用此參數進行備份時,要確保沒有任何DDL語句在執行,因為Innodb的數據一致性的隔離級別並不能隔離DDL操作

-l, --lock-tables  # 如果沒有使用非事務存儲引擎,使用此參數保證備份時數據的一致性,在備份時會依次鎖住每個數據庫下的所有表,一般用於MyISAM存儲引擎的備份,使用了此參數,在數據庫備份時,只能進行讀操作,由於此參數是鎖住一個數據庫下的所有表,備份時可以保證一個數據庫下的所有表的數據一致性,但不能保證整個Mysql實例下的所有數據庫的所有表的數據一致性,這也是為什么推薦使用Innodb引擎的一個原因
 
lock-tables 與 single-transaction 參數是互斥的,不能同時使用,所以數據庫中如果混合使用了Innodb表和MyISAM表就只能使用lock-tables來進行備份了

-x, --lock-all-tables  # 此參數可以對整個Mysql實例下的所有數據庫進行加鎖,可以避免lock-tables不能保證整個Mysql實例下的所有數據庫的所有表的數據一致性的問題,備份時同樣會將數據庫變為只讀的狀態

--master-data = [1/2]    # 無論是時間恢復還是新建slave實例都要用到這個參數,此參數有兩個可選值,當值為1時,備份中只記錄change_master語句,當值為2時,change_master語句會以注釋的形式出現在備份文件中;默認值為1,且當使用了此參數時會忽略lock-tables參數,在備份時如果使用了此參數,但是沒有使用single-transaction參數,則會自動使用lock-all-tables參數

如果我們要備份的數據庫中包含了存儲過程,觸發器,數據庫調度事件時,要備份這些數據庫對象時,必須指定以下參數才能對相應數據庫進行備份

-R, --routines     # 指定要備份的數據庫中存在的的存儲過程
--triggers     # 指定要備份的數據庫中存在的的觸發器
-E,--events      # 指定要備份的數據庫中存在的的調度事件

除了以上參數,還有一些參數在備份時也會用到

--hex-blob    # 因為mysqldump備份導出的是文本文件,如果導出的數據中含有以上類型,在文本格式下,有些字符是不可見的,如果使用了此參數使,將會對數據庫中所存在binary、varbinary、blob類型的數據以16進制的形式保存,就不會出現有些字符不可見的情況了
--tab =path    # 使用了此參數會在指定的路徑下對數據庫的每個表生成兩個文件,一個文件用於存儲表結構,另一個用於存儲表中的數據
-w, --where = '過濾條件'    # 導出指定條件的數據(只支持單表數據條件導出)


 備份賬號所需要的權限 : SELECT, RELOAD, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, PROCESS
如果使用--tab參數則還需要:FILE權限

3. 演示

首先創建備份用戶

create user 'backup'@'localhost' identified by '123456';

賦予用戶備份權限

grant select,reload,lock tables,replication client,show view,event,process on *.*  to 'backup'@'localhost';

使用mysqldump進行全量備份

備份某個數據庫

[root@localhost db_backup]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events mc_orderdb > mc_orderdb.sql
如果遇到以下問題
mysqldump: Error: Binlogging on server not active

MySQL中二進制日志功能默認是關閉,去/etc/my.cnf 文件中加入下面配置,開啟log_bin(數據庫的操作日志)功能,然后重啟mysql即可解決問題

log_bin=mysql-bin

之后使用 “systemctl start mysql” 重啟服務器,報錯

Job for mysql.service failed because the control process exited with error code. See "systemctl status mysql.service" and "journalctl -xe" for details.
解決方法:

在設置 log-bin 的時候同時需要設置 server-id 變量,即在配置文件中添加:

[mysqld]  
log-bin=mysql  
server-id=1  

然后再次重啟即可

補充知識
裝mysql,運行一段時間后,在mysql目錄下出現一堆類似mysql-bin.000***,從mysql-bin.000001開始一直排列下來,而且占用了大量硬盤空間,高達幾十個G. 對於這些超大空間占用量的文件我們應該怎么辦呢?

那么mysql數據庫文件夾中的mysql-bin.00001是什么文件?
mysql-bin.000001、mysql-bin.000002等文件是數據庫的操作日志,例如UPDATE一個表,或者DELETE一些數據,即使該語句沒有匹配的數據,這個命令也會存儲到日志文件中,還包括每個語句執行的時間,也會記錄進去的。

這些形如mysql-bin.00001的文件主要是用來做什么的呢?
1:數據恢復
如果你的數據庫出問題了,而你之前有過備份,那么可以看日志文件,找出是哪個命令導致你的數據庫出問題了,想辦法挽回損失。

2:主從服務器之間同步數據 
主服務器上所有的操作都在記錄日志中,從服務器可以根據該日志來進行,以確保兩個同步。

如果不想要這些文件應該怎么做呢?
1:只有一個mysql服務器,那么可以簡單的注釋掉這個選項就行了。
vi /etc/my.cnf把里面的 log-bin 這一行注釋掉,重啟mysql服務即可。

2:如果你的環境是主從服務器,那么就需要做以下操作了。 
A:在每個從屬服務器上,使用SHOW SLAVE STATUS來檢查它正在讀取哪個日志。
B:使用SHOW MASTER LOGS獲得主服務器上的一系列日志。
C:在所有的從屬服務器中判定最早的日志,這個是目標日志,如果所有的從屬服務器是更新的,就是清單上的最后一個日志。
D:清理所有的日志,但是不包括目標日志,因為從服務器還要跟它同步。

簡單地說,這些MySQL目錄下的形如mysql-bin.000***的文件時MySQL的事務日志。

刪除復制服務器已經拿走的binlog是安全的,一般來說網絡狀況好的時候,保留最新的那一個足以。

再次執行之前的備份命令,即可成功被封mc_orderdb數據庫下的所有表,我們可以查詢一下備份的SQL文件中是否包含所有表

[root@localhost db_backup]# grep "CREATE TABLE" mc_orderdb.sql
CREATE TABLE `order_cart` (
CREATE TABLE `order_customer_addr` (
CREATE TABLE `order_detail` (
CREATE TABLE `order_master` (
CREATE TABLE `region_info` (
CREATE TABLE `shipping_info` (
CREATE TABLE `warehouse_info` (
CREATE TABLE `warehouse_proudct` (
[root@localhost db_backup]# 

通過上面結果可以看出我們的幾個表都在其中

備份某個數據庫下的某個表

[root@localhost db_backup]#  mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events mc_orderdb order_master > order_master.sql
Enter password: 
[root@localhost db_backup]# ls
mc_orderdb.sql  order_master.sql

備份MySQL實例下的所有數據庫

[root@localhost db_backup]#  mysqldump -ubackup -p --master-data=1 --single-transaction --routines --triggers --events  --all-databases > mc.sql
Enter password: 
[root@localhost db_backup]# ls
mc_orderdb.sql  mc.sql  order_master.sql

由於master-data的值設置為1,change master命令並沒有被注釋掉,如果我們使用這個命令進行恢復,change master命令就會被執行,在一些情況下可能會造成一些錯誤,所以建議使用時最好還是設置為2

可以通過下面的命令查看,備份文件中包含哪些數據庫

[root@localhost db_backup]# grep "Current Database" mc.sql
-- Current Database: `mc_orderdb`
-- Current Database: `mc_productdb`
-- Current Database: `mc_userdb`
-- Current Database: `mysql`
[root@localhost db_backup]# 

使用-tab參數指定備份文件的位置

首先在/tmp 目錄下建立一個mc_orderdb目錄用來存放指定的備份文件,之所以使用在此目錄下建立目錄,是因為使用--tab參數時,用戶必須對目標目錄有可寫權限,而tmp目錄對任何用戶都有可寫權限

[root@localhost db_backup]#  mkdir -p /tmp/mc_orderdb

現在我們可以使用--tab參數指定備份路徑

[root@localhost db_backup]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events --tab="/tmp/mc_orderdb" mc_orderdb 
Enter password: 

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154;
mysqldump: Got error: 1045: Access denied for user 'backup'@'localhost' (using password: YES) when executing 'SELECT INTO OUTFILE'

可以發現,報錯了,其實我們在這之前還缺少一步,由於用戶需要有寫文件的權限,所以我們還需要對備份用戶賦予file權限

mysql> grant file on *.*  to 'backup'@'localhost';
Query OK, 0 rows affected (0.00 sec)

我們再次執行上面的備份命令

[root@localhost db_backup]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events --tab="/tmp/mc_orderdb" mc_orderdb 
Enter password: 

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=347;
mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing 'SELECT INTO OUTFILE'

可以很清楚地從提示看到是因為mysql服務啟用了–secure-file-priv,所以才無法執行。
那么–secure-file-priv又是什么呢,應該如何解決才能是它可以備份呢?
--secure-file-priv=name :
Limit LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE() to files within specified directory

可以看到secure-file-priv參數是用來限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()傳到哪個指定目錄的。

當secure_file_priv的值為null ,表示限制mysqld 不允許導入|導出

當secure_file_priv的值為/tmp/ ,表示限制mysqld 的導入|導出只能發生在/tmp/目錄下

當secure_file_priv的值沒有具體值時,表示不對mysqld 的導入|導出做限制

查看數據庫當前該參數的值

mysql> show global variables like '%secure%'; 
+--------------------------+-----------------------+
| Variable_name            | Value                 |
+--------------------------+-----------------------+
| require_secure_transport | OFF                   |
| secure_auth              | ON                    |
| secure_file_priv         | /var/lib/mysql-files/ |
+--------------------------+-----------------------+
3 rows in set (0.00 sec)

清楚地看到secure_file_priv 的值是NULL,說明此時限制導入導出的
所以應該改變該參數
可是查看了mysql.cnf中居然沒有對這個參數進行設定,就說明這個參數默認便是null
所以再mysql.cnf中的[mysqld]加入secure_file_priv =

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
datadir=/home/mysql/data    # mysql數據存放的目錄
socket=/var/lib/mysql/mysql.sock

# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

log_bin=mysql-bin
server-id=1  
secure_file_priv =

再重啟mysql服務

[root@localhost tmp]# systemctl restart mysqld

然后再查一下此時參數的值

mysql> show global variables like '%secure%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| require_secure_transport | OFF   |
| secure_auth              | ON    |
| secure_file_priv         |       |
+--------------------------+-------+
3 rows in set (0.01 sec)

已經是我們要的結果 ,現在我們再次執行備份命令

[root@localhost tmp]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events --tab="/tmp/mc_orderdb" mc_orderdb 
Enter password: 

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=154;
mysqldump: Got error: 1: Can't create/write to file '/tmp/mc_orderdb/order_cart.txt' (Errcode: 13 - Permission denied) when executing 'SELECT INTO OUTFILE'

結果還是權限被拒絕,無法寫入,我們可以查詢一下目錄mc_orderdb的權限,

[root@localhost tmp]# ls -lh mc_orderdb/
total 4.0K
-rw-r--r-- 1 root root 1.9K Jan 10 10:51 order_cart.sql

可以發現,是root用戶建立的目錄,我們需要修改其所屬用戶為mysql用戶,然后再次執行備份命令

[root@localhost tmp]#  chown mysql:mysql  mc_orderdb
[root@localhost tmp]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events --tab="/tmp/mc_orderdb" mc_orderdb 
Enter password: 

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=154;

--
-- Dumping events for database 'mc_orderdb'
--

--
-- Dumping routines for database 'mc_orderdb'
--
[root@localhost tmp]# 

可以發現,修改成功后即可備份成功

進入該目錄下會發現mc_orderdb數據庫下的每個表都有兩種文件,一種.sql結尾記錄是表結構,一種是.txt結尾的表數據

[root@localhost tmp]# cd mc_orderdb/
[root@localhost mc_orderdb]# ls
order_cart.sql  order_customer_addr.sql  order_detail.sql  order_master.sql  region_info.sql  shipping_info.sql  warehouse_info.sql  warehouse_proudct.sql
order_cart.txt  order_customer_addr.txt  order_detail.txt  order_master.txt  region_info.txt  shipping_info.txt  warehouse_info.txt  warehouse_proudct.txt

mysqldump如何使用全備where參數

使用場景

假設我們要對訂單id為1000到1050的主表進行修改,修改之前,我們需要先對數據進行備份,這里我們就可以使用where參數來完成此需求

執行命令進行備份

[root@localhost db_backup]# mysqldump -ubackup -p --master-data=2 --single-transaction  --where "order_id>1000 and order_id<1050" mc_orderdb  order_master > order_master_1000_1050.sql
Enter password: 

查看備份文件可以發現,訂單id是從1001開始的

在日程工作中我們不可能一直手工備份,所以我們需要將備份進行腳本話,然后使用計划任務去執行腳本

定義備份腳本

腳本文件

#!/bin/bash
###############Basic parameters##########################
DAY=`date +%Y%m%d`    # 記錄發生備份的當前日期
Environment=$(/sbin/ifconfig | grep "inet" | head -1 |grep -v "127.0.0.1" | awk '{print $2;}' )    # 當前主機的IP
USER="backup"
PASSWD="123456"
HostPort="3306"
MYSQLBASE="/home/mysql/"
DATADIR="/home/db_backup/${DAY}"    # 備份存放的目錄(/home/db_backup目錄下的以日期命名的子目錄中)
MYSQL=`/usr/bin/which mysql`    # 定義mysql命令的目錄
MYSQLDUMP=`/usr/bin/which mysqldump`    # 定義mysqldump命令的目錄
mkdir -p ${DATADIR}    # 創建存儲目錄

# 定義備份函數,使用到上面定義的變量

Dump(){
 ${MYSQLDUMP} --master-data=2 --single-transaction  --routines --triggers --events -u${USER} -p${PASSWD} -P${HostPort} ${database}  > ${DATADIR}/${Environment}-${database}.sql
 cd ${DATADIR}
 gzip ${Environment}-${database}.sql    # 對文件進行了壓縮
}

# 利用for循環對當前服務器下的每一個數據庫(排除了一些系統視圖所在的數據庫)分別來調用上面的Dump函數來進行備份

for db in `echo "SELECT schema_name FROM information_schema.schemata where schema_name not in ('information_schema','sys','performance_schema')" | ${MYSQL} -u${USER} -p${PASSWD} --skip-column-names`
do
   database=${db}
   Dump
done

執行腳本文件

[root@localhost home]# bash backup.sh 
mysql: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
[root@localhost home]# cd db_backup/
[root@localhost db_backup]# ls
20190110  mc_orderdb.sql  mc.sql  order_master_1000_1050.sql  order_master.sql
[root@localhost db_backup]# cd 20190110/
[root@localhost 20190110]# ls
172.17.0.1-mc_orderdb.sql.gz  172.17.0.1-mc_productdb.sql.gz  172.17.0.1-mc_userdb.sql.gz  172.17.0.1-mysql.sql.gz

可以看到結果已備份,可以使用crontab命令定時執行此腳本

如何恢復mysqldump備份的數據庫

方法一:

mysql -u -p dbname < backup.sql

方法二:

mysql>  source /tmp/backup.sql

使用mysqldump備份時,恢復的速度完全取決於MySQL實例執行SQL的速度和服務器的IO性能,並且恢復過程是單線程的,所以對於非常大的數據集來說,要恢復的話可能需要很長的時間

演示:

把剛才的全備數據恢復到bak數據庫中

創建bak_orderdb數據庫

[root@localhost db_backup]# mysql -uroot -p -e"create database bak_orderdb"
Enter password: 

將mc_orderdb備份的數據恢復到bak數據庫中

[root@localhost db_backup]#  mysql -uroot -p bak_orderdb < mc_orderdb.sql
Enter password: 
[root@localhost db_backup]# 

檢驗恢復結果的正確性

mysql> SELECT COUNT(*) FROM mc_orderdb.order_master;
+----------+
| COUNT(*) |
+----------+
|    10010 |
+----------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM bak_orderdb.order_master;
+----------+
| COUNT(*) |
+----------+
|    10010 |
+----------+
1 row in set (0.00 sec)

模擬誤操作並恢復數據

假設我們現在不小心刪除了mc_orderdb下的order_master中的10條數據,我們現在需要通過剛剛恢復的備份數據庫把這10條數據恢復回來

mysql> DELETE FROM mc_orderdb.order_master LIMIT 10;
Query OK, 10 rows affected (0.01 sec)

mysql>  SELECT COUNT(*) FROM mc_orderdb.order_master;
+----------+
| COUNT(*) |
+----------+
|    10000 |
+----------+
1 row in set (0.00 sec)

首先查出被誤刪的數據

SELECT a.* FROM bak_orderdb.order_master a 
LEFT JOIN mc_orderdb.order_master b ON a.order_id=b.order_id
WHERE b.order_id IS NULL;

然后執行insert語句將查出的數據插入回去

完整語句為

INSERT INTO mc_orderdb.order_master(
order_id,order_sn,customer_id,shipping_user,province,city,district,address,
payment_method,order_money,district_money,shipping_money,payment_money,
shipping_comp_name,shipping_sn,create_time,shipping_time,pay_time,receive_time,
order_status,order_point,invoice_title,modified_time)
SELECT a.* FROM bak_orderdb.order_master a 
LEFT JOIN mc_orderdb.order_master b ON a.order_id=b.order_id
WHERE b.order_id IS NULL;

執行結果:

mysql> INSERT INTO mc_orderdb.order_master(
    -> order_id,order_sn,customer_id,shipping_user,province,city,district,address,
    -> payment_method,order_money,district_money,shipping_money,payment_money,
    -> shipping_comp_name,shipping_sn,create_time,shipping_time,pay_time,receive_time,
    -> order_status,order_point,invoice_title,modified_time)
    -> SELECT a.* FROM bak_orderdb.order_master a 
    -> LEFT JOIN mc_orderdb.order_master b ON a.order_id=b.order_id
    -> WHERE b.order_id IS NULL;
Query OK, 10 rows affected (0.03 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> SELECT COUNT(*) FROM mc_orderdb.order_master;
+----------+
| COUNT(*) |
+----------+
|    10010 |
+----------+
1 row in set (0.00 sec)

對於一些靜態型數據我們可以這樣進行,但是對於一些時刻有數據在往表里寫的數據還原,就不能使用這種方式了

對於生產環境中,時刻有數據寫入的表如何進行數據恢復呢?

mysqldump單表備份恢復(使用了--tab參數備份的結果集)

需要進入mysql客戶端中

先恢復表結構

mysql>  source /tmp/mc_orderdb/region_info.sql;

再導入數據

mysql>     load data infile '/tmp/mc_orderdb/region_info.txt' info table region_info;

如何進行指定時間點的恢復

進行某一時間點的數據恢復,恢復到誤操作的時間

先決條件:

具有指定時間點前的mysqldump的全備
具有全備到指定時間點的mysql二進制日志

演示

首先我們需要有個數據庫的全備,此處我們對mc_orderdb 數據庫進行全備

[root@localhost db_backup]# mysqldump -ubackup -p --master-data=2 --single-transaction --routines --triggers --events mc_orderdb > mc_orderdb.sql

然后我們模擬一下生產環境中對數據庫的操作,這樣才能看到時間點恢復的效果

我們到mc_orderdb數據庫中新建一個統計表 t, uid列是用戶id,cnt 是用戶的總消費金額,將統計結果插入表t中后,模擬誤操作,刪除表t中的100行數據

mysql> use mc_orderdb
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> create table t(id int auto_increment not null,uid int,cnt decimal(8,2),primary key (id));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t(uid,cnt) select customer_id,sum(order_money) from order_master group by customer_id;
Query OK, 6314 rows affected (0.05 sec)
Records: 6314  Duplicates: 0  Warnings: 0

mysql> select count(*) from t;
+----------+
| count(*) |
+----------+
|     6314 |
+----------+
1 row in set (0.00 sec)

mysql> delete from t limit 100;
Query OK, 100 rows affected (0.01 sec)

mysql> select count(*) from t;
+----------+
| count(*) |
+----------+
|     6214 |
+----------+
1 row in set (0.01 sec)

先恢復一個最近的全備,進行全量數據恢復

[root@localhost db_backup]# mysql -uroot -p mc_orderdb < mc_orderdb.sql

然后查看mc_orderdb.sql文件中change master命令中

在進行這個全備時Mysql二進制日志的文件名(MASTER_LOG_FILE),以及時間點(MASTER_LOG_POS)

CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=154;

此處文件名為mysql-bin.000001,日志時間點為154;

下面我們要恢復這個時間點(154)之后到第一次刪除數據之前的數據

要找到這個時間點(154)之后到第一次刪除數據之前的數據

查看二進制日志,進行分析

[root@localhost db_backup]# cd /home/mysql
[root@localhost mysql]# mysqlbinlog --base64-output=decode-rows -vv --start-position=154 --database=mc_orderdb mysql-bin.000001| grep -B3 DELETE | more
#190110 13:44:54 server id 1  end_log_pos 83285 CRC32 0xf679d195 	Table_map: `mc_orderdb`.`t` mapped to number 119
# at 83285
#190110 13:44:54 server id 1  end_log_pos 84620 CRC32 0xa3408e6c 	Delete_rows: table id 119 flags: STMT_END_F
### DELETE FROM `mc_orderdb`.`t`
--
###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
###   @2=1 /* INT meta=0 nullable=1 is_null=0 */
###   @3=1042.34 /* DECIMAL(8,2) meta=2050 nullable=1 is_null=0 */
### DELETE FROM `mc_orderdb`.`t`
--
###   @1=2 /* INT meta=0 nullable=0 is_null=0 */
###   @2=3 /* INT meta=0 nullable=1 is_null=0 */
###   @3=803.37 /* DECIMAL(8,2) meta=2050 nullable=1 is_null=0 */

...省略

從中可以看到剛剛的二進制日志中的第一個DELETE,在它之前的日志結束點為84620

所以我們需要恢復的是154 到84620 之間,且數據庫為mc_orderdb ,日志所在文件名 為mysql-bin.000001的數據

我們通過mysqlbinlog 將這些數據導出來

[root@localhost mysql]# mysqlbinlog --start-position=154 --stop-position=84620 --database=mc_orderdb mysql-bin.000001 > mc_order_diff.sql

將數據導入恢復

[root@localhost mysql]# mysql -uroot -p mc_orderdb < mc_order_diff.sql

實時二進制日志備份

Mysql5.6版本之后,可以實時備份Binlog(二進制日志)

要使用這個功能,我們需要進行以下配置
首先新建一個用戶,這個用戶要有replication slave 權限

mysql>  grant replication slave on *.* to 'repl'@'localhost' identified by '123456';
Query OK, 0 rows affected, 1 warning (0.00 sec)

建立一個存儲備份的二進制日志文件的目錄

mkdir -p binlog_backup

最后在這個目錄下,執行以下命令,就可以實時的進行二進制日志的備份了

[root@localhost binlog_bak]# mysqlbinlog --raw --read-from-remote-server --stop-never --host localhost --port 3306 -u repl -p123456 mysql-bin.000001
mysqlbinlog: [Warning] Using a password on the command line interface can be insecure.

raw:代表這個命令是輸出的是raw格式的二進制日志
read-from-remote-server: 從mysql服務器上讀取這個日志
stop-never:備份的這個進程會持續在后台運行
最后的是指定要備份的二進制文件的名稱

此命令執行后這個終端會一直執行這個命令
現在打開另外一個終端,進入備份的目錄進行查看

[root@localhost mysql]# cd /home/binlog_bak/
[root@localhost binlog_bak]# ls
mysql-bin.000001

可以看到日志已經備份成功
進入mysql命令行,刷新日志

mysql> flush logs;
Query OK, 0 rows affected (0.01 sec)

mysql> show binary logs;
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| mysql-bin.000001 |   3560467 |
| mysql-bin.000002 |       154 |
+------------------+-----------+
2 rows in set (0.00 sec)

現在有兩個日志,我們再回到備份目錄進行查看

[root@localhost binlog_bak]# ls
mysql-bin.000001  mysql-bin.000002

可以看到最新的日志也已實時備份

三. xtrabackup

1. xtrabackup介紹

xtrabackup 物理備份工具,用於在線備份innodb存儲引擎的表

在所有表全是innodb存儲引擎表的情況下:
xtrabackup 可以保證在備份過程中,不影響表的讀寫操作
在最初的時候,xtrabackup 本身只支持對innodb存儲引擎表的備份,且只會備份數據文件,不會備份表的結構
innobackupex 是對xtrabackup 的插件,提供了備份表結構及其他配置信息的功能,並支持MyISAM表的備份,但也會鎖表

因為在當前的mysql版本下,還有一些系統表使用的是MyISAM存儲引擎,所以一般情況下使用的是innobackupex 腳本進行備份的

2. 安裝xtrabackup

3. 利用innobackupex 進行全備

innobackupex --user=backup --password=123456 --parallel=2 /home/db_backup/
[root@localhost home]# innobackupex --user=backup --password=123456 --parallel=2 /home/db_backup/
xtrabackup: recognized server arguments: --datadir=/home/mysql --log_bin=mysql-bin --server-id=1 --parallel=2 
xtrabackup: recognized client arguments: --datadir=home/mysql --log_bin=mysql-bin --server-id=1 --parallel=2 
190110 15:15:30 innobackupex: Starting the backup operation

IMPORTANT: Please check that the backup run completes successfully.
           At the end of a successful backup run innobackupex
           prints "completed OK!".

... 省略

190110 15:15:37 [00] Writing /home/db_backup/2019-01-10_15-15-30/xtrabackup_info
190110 15:15:37 [00]        ...done
xtrabackup: Transaction log of lsn (79088947) to (79088956) was copied.
190110 15:15:37 completed OK!

當看到completed OK!代表備份已完成

和mysqldump單線程備份不同,我們可以通過parallel參數指定備份的線程數
/home/db_backup/ 是我們指定的備份文件的存儲目錄

xtrabackup 會已當前時間在目標目錄中生成一個子目錄用來存放當前的備份文件

我們進入目錄中查看一下

[root@localhost db_backup]# ls
20190110  2019-01-10_15-15-30  mc_orderdb.sql  mc.sql  order_master_1000_1050.sql  order_master.sql
[root@localhost db_backup]# cd 2019-01-10_15-15-30/
[root@localhost 2019-01-10_15-15-30]# ls
backup-my.cnf  ib_buffer_pool  mc_orderdb    mc_userdb  performance_schema  xtrabackup_binlog_info  xtrabackup_info
bak_orderdb    ibdata1         mc_productdb  mysql      sys                 xtrabackup_checkpoints  xtrabackup_logfile

與原數據文件目錄相比,少了ib_logfile0等日志文件

xtrabackup_binlog_info 比較重要,其中記錄了備份的日志名和日志點,相當於mysqldump中設置master-data參數的作用

如果我們不想以時間戳的形式自動生成子目錄,我們可以通過指定--no-timestamp參數來實現

innobackupex --user=backup --password=123456 --parallel=2  /home/db_backup/2019-01-10 --no-timestamp
[root@localhost db_backup]#  innobackupex --user=backup --password=123456 --parallel=2  /home/db_backup/2019-01-10 --no-timestamp
[root@localhost db_backup]# ls
20190110  2019-01-10  2019-01-10_15-15-30  mc_orderdb.sql  mc.sql  order_master_1000_1050.sql  order_master.sql
[root@localhost db_backup]# cd 2019-01-10
[root@localhost 2019-01-10]# ls
backup-my.cnf  ib_buffer_pool  mc_orderdb    mc_userdb  performance_schema  xtrabackup_binlog_info  xtrabackup_info
bak_orderdb    ibdata1         mc_productdb  mysql      sys                 xtrabackup_checkpoints  xtrabackup_logfile
[root@localhost 2019-01-10]# 

4. 利用innobackupex 進行全備的恢復

innobackupex  --apply-log /path/to/BACKUP-DIR
mv /path/to/BACKUP-DIR /home/mysql/data

利用xtrabackup備份產生的備份集並不能直接用於數據庫的恢復
進行數據庫恢復之前,我們必須把備份中產生的備份集 進行應用,此處我們使用的是備份文件的目錄偽2019-01-10

[root@localhost db_backup]# innobackupex --apply-log /home/db_backup/2019-01-10

[root@localhost db_backup]# cd 2019-01-10
[root@localhost 2019-01-10]# ls
backup-my.cnf   ib_logfile0  mc_productdb        sys                           xtrabackup_info
bak_orderdb     ib_logfile1  mc_userdb           xtrabackup_binlog_info        xtrabackup_logfile
ib_buffer_pool  ibtmp1       mysql               xtrabackup_binlog_pos_innodb  xtrabackup_master_key_id
ibdata1         mc_orderdb   performance_schema  xtrabackup_checkpoints

可以發現,使用了上面的命令后,備份集中多出了之前缺少的ib_logfile0等文件

使用xtrabackup進行數據庫恢復時,我們必須對數據庫實例進行重啟
先停用mysql服務

然后將原數據庫文件所在的文件夾重命名為data_bak,然后將此時的 2019-01-10文件夾移動到data_bak所在的文件夾,並重命名為data,即覆蓋了原來的文件

然后 chown -R mysql:mysql data
對文件夾修改所屬用戶

最后重啟mysql服務即可

5. 利用innobackupex 進行增量備份

innobackupex --user=backup --password=123456 /home/db_backup    # 先進行全備
  
innobackupex --user=backup --password=123456 --incremental  /home/db_backup/  --incremental-basedir=/home/db_backup/2019-01-10/

incremental :表示我們要進行的是一個全備, 指定全備的目錄
incremental-basedir :指定增量備份所依賴的數據基礎的備份目錄,這個增量備份所依賴的上一個全備

演示:

先進行全備

innobackupex --user=backup --password=123456 /home/db_backup  

在 /home/db_backup 目錄下生產了一個新的全備子目錄 2019-01-10_16-19-37

再依賴上個全量備份進行增量備份

innobackupex --user=backup --password=123456 --incremental  /home/db_backup/  --incremental-basedir=/home/db_backup/2019-01-10_16-19-37/

此時在 /home/db_backup 目錄下生產了一個增量備分的子目錄2019-01-10_16-22-09
增量備份會把自上一個全備后的數據變更記錄下來

然后再進行一次增量備份,此時的命令和前面基本相同,只是所基於的增量備份的數據目錄要變成上次增量備份生成的目錄

innobackupex --user=backup --password=123456 --incremental  /home/db_backup/  --incremental-basedir=/home/db_backup/2019-01-10_16-22-09/

此時,第二次增量備份的生成的目錄名為2019-01-10_16-24-09

6. 利用innobackupex 進行增量恢復

innobackupex  --apply-log  --redo-only 全備目錄

我們要循環的在多次增量備份中應用上面步驟

 innobackupex  --apply-log  --redo-only 全備目錄  --incremental-dir=第一次增量目錄

所有增量備份的都應有了上面的命令后,就可以像全備一樣,在全備目錄上再進行崩潰恢復的過程

    innobackupex  --apply-log /path/to/BACKUP-DIR
    mv /path/to/BACKUP-DIR /home/mysql/data

最后和全備一樣,要用全備目錄替換mysql數據庫目錄

演示

下面演示只恢復到第一次備份

[root@localhost db_backup]#   innobackupex  --apply-log  --redo-only    /home/db_backup/2019-01-10_16-19-37

[root@localhost db_backup]#   innobackupex  --apply-log  --redo-only    /home/db_backup/2019-01-10_16-19-37  --incremental-basedir=/home/db_backup/2019-01-10_16-22-09

[root@localhost db_backup]#   innobackupex  --apply-log   /home/db_backup/2019-01-10_16-19-37 

[root@localhost db_backup]#  mv 2019-01-10_16-19-37  /home/mysql

[root@localhost db_backup]#  cd  /home/mysql

[root@localhost mysql]#  systemctl stop  mysqld

[root@localhost mysql]#  mv 2019-01-10_16-19-37 data

[root@localhost mysql]# chown -R mysql:mysql data

[root@localhost mysql]#  systemctl start mysqld

四. 要制定備份計划

每天凌晨對數據庫進行一次全備
實時對二進制日志進行遠程備份


免責聲明!

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



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