前言
看完本文,删庫跑路!? 再也不可能發生了!一定要養成風險操作備份資料的習慣,避免恢複資料浪費時間。
本文内容:
- binlog 的作用
- mysqldump 和 mysqlbinlog 做資料備份和資料恢複。
- XtraBackup 全量備份和增量備份
binlog

binlog 作用及配置
Mysql 的
binlog
(二進制日志) 是
Server
層的,不管你的存儲引擎是什麼都可以使用
binlog
。
binlog
記錄的是資料庫
DML
和
DDL
修改的資料内容,也可以用于資料的備份與恢複。一般我們會用
binlog
也用于主從複制,從庫請求主庫的
binlog
寫入到自己的中繼日志,然後将中繼日志轉換為
sql
,然後将 sql 執行在從庫執行。
-- 檢視是否開啟 binlog
SHOW VARIABLES LIKE '%log_bin%'
mysql> SHOW VARIABLES LIKE '%log_bin%';
+---------------------------------+-----------------------------+
| Variable_name | Value |
+---------------------------------+-----------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/binlog |
| log_bin_index | /var/lib/mysql/binlog.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+-----------------------------+
開啟二進制日志配置
log_bin
配置是否啟用
binlog
Mysql 8.0
預設開啟
binlog
log_bin_index
配置的是
binlog
日志檔案的索引資訊。這個配置最好配置了之後不要修改。
log_bin_basename
binlog
日志的基礎路徑名稱。
server_id
這個也需要配置,在一個叢集中這個數字不能重複。
sql_log_bin
配置目前會話 DML 和 DDL 語句是否記錄。
[root@centos-7 mysql]# pwd
/var/lib/mysql
[root@centos-7 mysql]# ll | grep binlog
-rw-r-----. 1 mysql mysql 16162 11月 21 15:58 binlog.000013
-rw-r-----. 1 mysql mysql 179 11月 21 15:58 binlog.000014
-rw-r-----. 1 mysql mysql 3765 11月 22 14:42 binlog.000015
-rw-r-----. 1 mysql mysql 1700 11月 23 23:40 binlog.000016
-rw-r-----. 1 mysql mysql 64 11月 22 14:42 binlog.index
[root@centos-7 mysql]#
binlog 日志格式
-- 檢視目前 binlog 檔案存儲什麼資料
SHOW VARIABLES LIKE '%binlog_format%';
binlog
日志格式有以下三種
STATEMENT
記錄的是 sql 語句。
ROW
Mysql 8.0
預設采用這個格式。記錄每行的修改。相較于
STATEMENT
它可能記錄的内容會更多,但是主從複制時更安全。
比如全表更新
update test set a=1;
STATEMENT
隻會記錄這個
sql
,而
ROW
會記錄所有資料的修改。
MIXED
當需要時,Mysql 将日志格式從
STATEMENT
切換為
ROW
比如說更新語句可能記錄為邏輯 sql (
STATEMENT
),而插入語句記錄為(
ROW
) 格式。
binlog 日志格式驗證
建立一張表,插入 10 w 資料
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
DROP PROCEDURE if EXISTS insertData;
CREATE PROCEDURE insertData ( )
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i < 10000 DO
SET i = i + 1;
INSERT INTO account ( username, age )VALUES( '測試', 12 );
END WHILE;
END;
CALL insertData ( );
-
在binlog_format
模式下記錄的是每行資料的修改ROW
mysql> SHOW BINLOG EVENTS IN 'binlog.000018' limit 10;
+---------------+-------+----------------+-----------+-------------+--------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+-------+----------------+-----------+-------------+--------------------------------------+
| binlog.000018 | 4 | Format_desc | 1 | 125 | Server ver: 8.0.21, Binlog ver: 4 |
| binlog.000018 | 125 | Previous_gtids | 1 | 156 | |
| binlog.000018 | 156 | Anonymous_Gtid | 1 | 236 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000018 | 236 | Query | 1 | 322 | BEGIN |
| binlog.000018 | 322 | Table_map | 1 | 386 | table_id: 99 (ceshi2.account) |
| binlog.000018 | 386 | Update_rows | 1 | 8600 | table_id: 99 |
| binlog.000018 | 8600 | Update_rows | 1 | 16814 | table_id: 99 |
| binlog.000018 | 16814 | Update_rows | 1 | 25028 | table_id: 99 |
| binlog.000018 | 25028 | Update_rows | 1 | 33242 | table_id: 99 |
| binlog.000018 | 33242 | Update_rows | 1 | 41456 | table_id: 99 |
+---------------+-------+----------------+-----------+-------------+--------------------------------------+
-
binlog_format
模式下記錄的是 sqlSTATEMENT
flush logs;
update ceshi2.account set username='2';
mysql> SHOW BINLOG EVENTS IN 'binlog.000019' limit 10;
+---------------+-----+----------------+-----------+-------------+----------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+-----+----------------+-----------+-------------+----------------------------------------+
| binlog.000019 | 4 | Format_desc | 1 | 125 | Server ver: 8.0.21, Binlog ver: 4 |
| binlog.000019 | 125 | Previous_gtids | 1 | 156 | |
| binlog.000019 | 156 | Anonymous_Gtid | 1 | 235 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000019 | 235 | Query | 1 | 324 | BEGIN |
| binlog.000019 | 324 | Query | 1 | 446 | update ceshi2.account set username='2' |
| binlog.000019 | 446 | Xid | 1 | 477 | COMMIT /* xid=300671 */ |
+---------------+-----+----------------+-----------+-------------+----------------------------------------+
binlog 操作
檢視所有的 binlog
-- 檢視連結的資料庫 binlog 檔案資訊
SHOW BINARY LOGS;
SHOW MASTER LOGS;
mysql> SHOW BINARY LOGS;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000013 | 16162 | No |
| binlog.000014 | 179 | No |
| binlog.000015 | 3765 | No |
| binlog.000016 | 1700 | No |
+---------------+-----------+-----------+
檢視目前正在寫入的 binlog
-- 檢視目前正在寫入的 binlog 檔案資訊
SHOW MASTER STATUS;
mysql> SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000016 | 1700 | | | |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
生成新的 binlog
-- 重新整理産生新的日志檔案
FLUSH LOGS;
-- 原來的日志檔案是 binlog.000016
mysql> SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000017 | 156 | | | |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
檢視 binlog 中的操作
SHOW BINLOG EVENTS
[IN 'log_name']
[FROM pos]
[LIMIT [offset,] row_count]
mysql> show binlog events limit 100,3;
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------+
| binlog.000013 | 9382 | Query | 1 | 9539 | use `ceshi`; GRANT SELECT ON `ceshi`.`test2` TO 'db_dev'@'localhost' /* xid=1023 */ |
| binlog.000013 | 9539 | Anonymous_Gtid | 1 | 9616 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000013 | 9616 | Query | 1 | 9711 | use `ceshi`; FLUSH PRIVILEGES |
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------+
binlog 落盤時機
Mysql 中有很多
Buffer Pool
(可以簡單了解為記憶體),為了提高資料庫性能,一般送出事務之後,二進制日志先寫入
Buffer Poll
,在寫入到二進制檔案中。
如果二進制日志沒有落盤,那麼二進制日志有可能丢失,從庫進行複制時會丢失資料。
參數
sync_binlog
配置寫入
Buffer Poll
多少次的時候調用系統調用
fsync
将記憶體中的二進制日志資料落盤。
-
表示送出事務的時候同步将二進制日志資料落盤。配合sync_binlog=1
(控制 redo log 落盤) 資料安全。innodb_flush_log_at_trx_commit=1
-
表示送出了 N 個二進制資料時才将日志資料落盤。也有人将其設定為 2,提高并發性,系統崩潰時可能丢失二進制日志資料。sync_binlog=N
-
表示由作業系統 IO 排程來決定日志什麼時候落盤。一般沒人采用這個。sync_binlog=0
Mysql 備份和恢複
為了避免意外情況發生導緻資料丢失,資料庫需要定時全備和增量備份。以便于可以将資料庫恢複到任意時間點的資料。
根據備份方法的不同可以劃分為:
- 熱備(Hot Backup)
- 冷備 (Clod Backup)
熱備是在資料庫正在運作時直接備份,對業務的影響不大。
冷備需要停止 Mysql 進行備份,速度較快。可以在從庫進行冷備。
根據備份後的檔案内容可以劃分為:
- 邏輯備份,資料庫執行的 sql 内容
- 檔案備份,備份資料庫的實體檔案
一般我們會定時對資料執行備份腳本,然後将備份的内容壓縮發送到存儲檔案的伺服器,比如
OSS
備份與恢複使用到程式
- mysqldump,對資料庫進行不停機執行邏輯備份及恢複
- mysqlbinlog,操作 binlog 日志,使資料恢複到某個時間點的資料
- xtrabackup,percona 開源工具,對資料庫不停機進行檔案備份
mysqldump 使用
備份某些資料庫
mysqldump --master-data --single-transaction --databases ceshi2 ceshi -h10.211.55.8 -uroot -pMysql@12345678 > backup.sql
備份所有資料庫
mysqldump --master-data --single-transaction --all-databases -h10.211.55.8 -uroot -pMysql@12345678 > backup.sql
參數說明
-
用于全是--single-transaction
表的備份。備份開始執行前InnoDB
會開啟事務,由于START TRANSACTION
的特性這種備份不會影響資料庫讀寫,而且還保證了備份期間資料的一緻性MVCC
-
為 1 時記錄--master-data
語句,可以在從庫中使用備份的檔案,比如新增加一個從庫,就可以在從庫上執行這個備份的資料。為 2 時 會注釋CHANGE MASTER
CHANGE MASTER
-
鎖住單個資料庫中所有表,隻允許讀取資料。為了保證備份時資料的一緻性。因為隻能鎖住單個資料庫,如果有多個資料庫就不能保證資料的一緻性了。當資料庫采用的存儲引擎既有--lock-tables
InnoDB
時需要使用這個屬性MyISAM
-
鎖住備份所有資料庫的表,能保證多個資料庫資料的一緻性。--lock-all-tables
-
可以指定備份哪些資料庫執行個體--databases
-
備份連接配接中所有的資料庫執行個體。--all-databases
-
備份事件排程器--evnets
-
備份存儲過程和存儲函數--routines
-
備份觸發器--triggers
-
導出之前重新整理日志,因為有的資料在記憶體中,可能還沒有寫入到二進制日志中--flush-logs
mysqlbinlog 使用
mysqlbinlog 可以解析
binlog
生成 sql語句。
# 在本地生成 sql
mysqlbinlog --disable-log-bin /Users/zhangpanqin/Desktop/binlog.000019 > test.sql
mysqlbinlog --disable-log-bin /Users/zhangpanqin/Desktop/binlog.000019 > test.sql
# 根據日志的位置
mysqlbinlog binlog.000019 --disable-log-bin --start-position 775 > 775.sql
mysqlbinlog binlog.000019 --disable-log-bin --start-position 477 --stop-position 556 > 477-556.sql
# 根據時間
mysqlbinlog binlog.000019 --start-date='2017-12-19 10:10:00' --stop-date='2017-12-19 18:52:00' > aa.sql
# 連結遠端使用
mysqlbinlog --disable-log-bin --read-from-remote-server --host=10.211.55.8 --user=root --password=Mysql@12345678 binlog.000019 binlog.000020> remote_test.sql
-
指定從哪個位置開始--start-position
-
--stop-position
-
指定開始時間--start-datetime
-
指定結束時間--stop-datetime
-
生成的 sql 語句中,添加--disable-log-bin
,執行轉換的 sql 時,不會生成二進制日志SET SQL_LOG_BIN=0
-
從遠端伺服器讀取--read-from-remote-server
資料恢複
一般我們會使用
mysqldump
進行一個全量備份,在這個全量備份的基礎上,從
binlog
提取後續 sql 進行資料恢複。
模拟一個場景
1、比如我們在某個
2020-11-28 16:30:00
進行了全量備份。
2、
2020-11-28 16:35:00
删除了
account
表中全部資料
3、删除之後不知道,又插入了兩條資料
INSERT INTO `ceshi2`.`account`(`id`, `username`, `age`) VALUES (11111111, '删除全庫之後插入', 11);
INSERT INTO `ceshi2`.`account`(`id`, `username`, `age`) VALUES (11111112, 'asdfasd', 12);
恢複資料的時候,為避免恢複操作寫入到二進制日志中去,需要暫時關閉二進制日志,恢複會話期間不寫入二進制日志
SET SQL_LOG_BIN=0;
SHOW VARIABLES LIKE '%sql_log_bin%';
現在開始對資料庫進行資料恢複
- 開始恢複之前先
重新整理新的二進制日志flush logs
mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000020 | 156 | | | |
+---------------+----------+--------------+------------------+-------------------+
- 設定目前會話不記錄二進制日志,并恢複全備資料
(echo "SET SQL_LOG_BIN=0;";cat /Users/zhangpanqin/Desktop/backup.sql) | mysql -u root -h 10.211.55.8 -pMysql@12345678 -f
- 檢視 backup.sql 記錄的是什麼時候備份的資料
/*CHANGE MASTER TO MASTER_LOG_FILE='binlog.000019', MASTER_LOG_POS=477;*/
- 使用 mysqlbinlog 導出
從位置binlog
開始的 sql477
-- 笨的方法就是,檢視删除的 sql 語句
SHOW BINLOG EVENTS IN 'binlog.000019' FROM 477 LIMIT 0,10;
mysql> SHOW BINLOG EVENTS IN 'binlog.000019' FROM 477 LIMIT 0,10;
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------+
| binlog.000019 | 477 | Anonymous_Gtid | 1 | 556 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000019 | 556 | Query | 1 | 642 | BEGIN |
| binlog.000019 | 642 | Query | 1 | 744 | use `ceshi2`; DELETE FROM `account` |
| binlog.000019 | 744 | Xid | 1 | 775 | COMMIT /* xid=300922 */ |
| binlog.000019 | 775 | Anonymous_Gtid | 1 | 854 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000019 | 854 | Query | 1 | 940 | BEGIN |
| binlog.000019 | 940 | Query | 1 | 1126 | use `ceshi2`; INSERT INTO `ceshi2`.`account`(`id`, `username`, `age`) VALUES (11111111, '删除全庫之後插入', 11) |
| binlog.000019 | 1126 | Xid | 1 | 1157 | COMMIT /* xid=301033 */ |
| binlog.000019 | 1157 | Anonymous_Gtid | 1 | 1236 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000019 | 1236 | Query | 1 | 1322 | BEGIN |
+---------------+------+----------------+-----------+-------------+-------------------------------------------------------------------------------------------------------------------------+
-- 導出 477-556 之間的 sql
mysqlbinlog binlog.000019 --start-position 477 --stop-position 556 > 477-556.sql
-- 導出 從 775 開始的 sql
mysqlbinlog binlog.000019 --start-position 775> 775.sql
這裡比較好的做法就是直接使用工具直接解析 sql
binlog2sqlhttps://github.com/danfengcao/binlog2sql
- 執行剩下的 sql
(echo "SET SQL_LOG_BIN=0;";cat /Users/zhangpanqin/Desktop/477-556.sql) | mysql -u root -h 10.211.55.8 -pMysql@12345678 -f
(echo "SET SQL_LOG_BIN=0;";cat /Users/zhangpanqin/Desktop/775.sql) | mysql -u root -h 10.211.55.8 -pMysql@12345678 -f
- 檢視 binlog 日志,沒有添加二進制日志到資料庫中,不影響從庫
mysql> SHOW MASTER STATUS;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000020 | 156 | | | |
+---------------+----------+--------------+------------------+-------------------+
XtraBackup 使用
XtraBackup 隻能備份InnoDB和XtraDB 兩種資料表。
安裝
系統環境:Centos 7 x86_64
資料庫:Mysql 8.0.21
由以上環境決定了
xtrabackup
需要安裝 8.0.14 版本。
https://www.percona.com/doc/percona-xtrabackup/8.0/installation/yum_repo.html
wget https://downloads.percona.com/downloads/Percona-XtraBackup-LATEST/Percona-XtraBackup-8.0.14/binary/redhat/7/x86_64/percona-xtrabackup-80-8.0.14-1.el7.x86_64.rpm
yum localinstall percona-xtrabackup-80-8.0.14-1.el7.x86_64.rpm
# 驗證版本
xtrabackup --version
指令講解
-
備份操作,備份資料到--backup
指定的目錄。--target-dir
-
恢複資料執行的階段。--prepare
-
指定備份時占用的記憶體,--use-memory=4G。--use-memory
-
将準備好的資料檔案複制到 mysql datadir 目錄。--copy-back
-
阻止復原未完成的事務`--apply-log-only
全量備份
建立備份使用的使用者
CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY 'Mysql@12345678';
GRANT BACKUP_ADMIN, PROCESS, RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'xtrabackup'@'localhost';
GRANT SELECT ON performance_schema.log_status TO 'xtrabackup'@'localhost';
FLUSH PRIVILEGES;
xtrabackup 全量備份
xtrabackup --host=localhost --user=xtrabackup --password=Mysql@12345678 --backup --target-dir=/opt/test22/backup
全量資料恢複
# 先停止資料庫
systemctl stop mysqld
# 恢複資料執行的準備
xtrabackup --host=localhost --user=xtrabackup --password=Mysql@12345678 --prepare --target-dir=/opt/test22/backup
# 備份資料庫檔案,并删除資料庫資料目錄下的檔案
cp -r /var/lib/mysql{,"$(date '+%Y-%m-%d %H:%M:%S')"_bak} && rm -fr /var/lib/mysql/*
# 恢複資料
xtrabackup --host=localhost --user=xtrabackup --password=Mysql@12345678 --copy-back --target-dir=/opt/test22/backup
# 檢視 /var/lib/mysql 目錄下所有檔案的所屬人,需要改成 mysqld 運作的使用者
chown -R mysql:mysql /var/lib/mysql
# 啟動 mysql 資料庫
systemctl start mysqld
增量備份
在全量備份的基礎上,增量備份。
# 建立全量備份在那個目錄下
mkdir -p /opt/xtrabackup_mysql/full_data_dir
# 全量基礎之後的增量資料一次
mkdir -p /opt/xtrabackup_mysql/increment_data_dir
# 在上一次增量備份的基礎上在增量備份一次
mkdir -p /opt/xtrabackup_mysql/increment_data_dir_2
# 全量備份
xtrabackup --defaults-file=/etc/my.cnf --host=localhost --user=xtrabackup --password=Mysql@12345678 --backup --parallel=3 --target-dir=/opt/xtrabackup_mysql/full_data_dir
# 全量備份之後,操作資料。
# 做增量備份
xtrabackup --defaults-file=/etc/my.cnf --host=localhost --user=xtrabackup --password=Mysql@12345678 --backup --parallel=3 --target-dir=/opt/xtrabackup_mysql/increment_data_dir --incremental-basedir=/opt/xtrabackup_mysql/full_data_dir
# 操作了資料之後,在上一次增量備份基礎上做第二次增量備份
xtrabackup --defaults-file=/etc/my.cnf --host=localhost --user=xtrabackup --password=Mysql@12345678 --backup --parallel=3 --target-dir=/opt/xtrabackup_mysql/increment_data_dir_2 --incremental-basedir=/opt/xtrabackup_mysql/increment_data_dir
增量備份資料恢複
# 停止資料庫
systemctl stop mysqld
# 準備全備份日志資料
xtrabackup --defaults-file=/etc/my.cnf --prepare --apply-log-only --target-dir=/opt/xtrabackup_mysql/full_data_dir
# 合并第一次增量備份資料到全量中,注意路徑别寫錯了
xtrabackup --defaults-file=/etc/my.cnf --prepare --apply-log-only --target-dir=/opt/xtrabackup_mysql/full_data_dir --incremental-dir=/opt/xtrabackup_mysql/increment_data_dir
# 合并第二次增量備份資料到全量中,注意路徑。最後一次不需要添加 --apply-log-only
xtrabackup --defaults-file=/etc/my.cnf --prepare --target-dir=/opt/xtrabackup_mysql/full_data_dir --incremental-dir=/opt/xtrabackup_mysql/increment_data_dir_2
# 将原來資料庫備份
cp -r /var/lib/mysql{,"$(date '+%Y-%m-%d %H:%M:%S')"_bak} && rm -fr /var/lib/mysql/*
-- 拷回資料
xtrabackup --defaults-file=/etc/my.cnf --copy-back --target-dir=/opt/xtrabackup_mysql/full_data_dir
# 修改mysql 資料檔案的權限為 mysql
chown -R mysql:mysql /var/lib/mysql
# 啟動資料庫
systemctl start mysqld