1. 程式人生 > >MySQL 5.7 - 通過 BINLOG 恢復資料

MySQL 5.7 - 通過 BINLOG 恢復資料

日常開發,運維中,經常會出現誤刪資料的情況。誤刪資料的型別大致可分為以下幾類:

  • 使用 delete 誤刪行
  • 使用 drop table 或 truncate table 誤刪表
  • 使用 drop database 語句誤刪資料庫
  • 使用 rm 命令誤刪整個 MySQL 例項。

不同的情況,都會有其優先的解決方案:

  • 針對誤刪行,可以通過 Flashback 工具將資料恢復
  • 針對誤刪表或庫,一般採用通過 BINLOG 將資料恢復。
  • 而對於誤刪 MySQL 例項,則需要我們搭建 HA 的 MySQL 叢集,並保證我們的資料跨機房,跨城市儲存。

本篇主要討論的內容是誤刪表或者庫,會先介紹有關 BINLOG 的操作命令,然後會對誤刪表的這種情況進行實際的模擬。

BINLOG 常見操作命令

BINLOG 的查詢方式一般分為兩種,一種是進入 MySQL 控制檯進行查詢,另一種是通過 MySQL 提供的工具 mysqlbinlog 進行查詢,兩者的不同會在下面介紹。

通過 MySQL Cli 查詢 BINLOG 資訊

在 cli 中,常見的命令如下:

# 查詢 BINLOG 格式
show VARIABLES like 'binlog_format';

# 查詢 BINLOG 位置
show VARIABLES like 'datadir';

# 查詢當前資料庫中 BINLOG 名稱及大小
show binary logs;

# 檢視 master 正在寫入的 BINLOG 資訊
show master status\G;

# 通過 offset 檢視 BINLOG 資訊
show BINLOG events in 'mysql-bin.000034' limit 9000,  10;

# 通過 position 檢視 binlog 資訊
show BINLOG events in 'mysql-bin.000034' from 1742635 limit 10;

使用 show BINLOG events 的問題:

  • 使用該命令時,如果當前 binlog 檔案很大,而且沒有指定 limit,會引發對資源的過度消耗。因為 MySQL 客戶端需要將 binlog 的全部內容處理,返回並顯示出來。為了防止這種情況,mysqlbinlog 工具是一個很好的選擇。

通過 mysqlbinlog 查詢 BINLOG 資訊

在介紹 mysqlbinlog 工具使用前,先來看下 BINLOG 檔案的內容:

# 查詢 BINLOG 的資訊
mysqlbinlog  --no-defaults mysql-bin.000034 | less
# at 141
#100309  9:28:36 server id 123  end_log_pos 245
  Query thread_id=3350  exec_time=11  error_code=0
  • at 表示 offset 或者說事件開始的起始位置
  • 100309 9:28:36 server id 123 表示 server 123 開始執行事件的日期
  • end_log_pos 245 表示事件的結束位置 + 1,或者說是下一個事件的起始位置。
  • exec_time 表示在 master 上花費的時間,在 salve 上,記錄的時間是從 Master 記錄開始,一直到 Slave 結束完成所花費的時間。
  • rror_code=0 表示沒有錯誤發生。

在大致瞭解 binlog 的內容後,mysqlbinlog 的用途有哪些?:

  • mysqlbinlog 可以作為代替 cli 讀取 binlog 的工具。
  • mysqlbinlog 可以將執行過的 SQL 語句輸出,用於資料的恢復或備份。

查詢 BINLOG 日誌:

# 查詢規定時候後發生的 BINLOG 日誌
mysqlbinlog --no-defaults --base64-output=decode-rows -v  --start-datetime  "2019-11-22 14:00:00" --database sync_test  mysql-bin.000034 | less

匯出 BINLOG 日誌,用於分析和排查 sql 語句:

mysqlbinlog --no-defaults --base64-output=decode-rows -v  --start-datetime  "2019-11-22 14:00:00" --database sync_test  mysql-bin.000034 > /home/mysql_backup/binlog_raw.sql

匯入 BINLOG 日誌

# 通過 BINLOG 進行恢復。
mysqlbinlog --start-position=1038 --stop-position=1164 --database=db_name  mysql-bin.000034 | mysql  -u cisco -p db_name

# 通過 BINLOG 匯出的 sql 進行恢復。
mysql -u cisco -p db_name < binlog_raw.sql.sql

mysqlbinlog 的常用引數:

  • --database 僅僅列出配置的資料庫資訊
  • --no-defaults 讀取沒有選項的檔案, 指定的原因是由於 mysqlbinlog 無法識別 BINLOG 中的 default-character-set=utf8 指令
  • --offset 跳過 log 中 N 個條目
  • --verbose 將日誌資訊重建為原始的 SQL 陳述。
    • -v 僅僅解釋行資訊
    • -vv 不但解釋行資訊,還將 SQL 列型別的註釋資訊也解析出來
  • --start-datetime 顯示從指定的時間或之後的時間的事件。
    • 接收 DATETIME 或者 TIMESTRAMP 格式。
  • --base64-output=decode-rows 將 BINLOG 語句中事件以 base-64 的編碼顯示,對一些二進位制的內容進行遮蔽。
    • AUTO 預設引數,自動顯示 BINLOG 中的必要的語句
    • NEVER 不會顯示任何的 BINLOG 語句,如果遇到必須顯示的 BINLOG 語言,則會報錯退出。
    • DECODE-ROWS 顯示通過 -v 顯示出來的 SQL 資訊,過濾到一些 BINLOG 二進位制資料。

MySQL Cli 和 mysqlbinlog 工具之間的比較

如果想知道當前 MySQL 中正在寫入的 BINLOG 的名稱,大小等基本資訊時,可以通過 Cli 相關的命令來查詢。

但想查詢,定位,恢復 BINLOG 中具體的資料時,要通過 mysqlbinlog 工具,因為相較於 Cli 來說,mysqlbinlog 提供了 --start-datetime--stop-position 等這樣更為豐富的引數供我們選擇。這時 Cli 中 SHOW BINLOG EVENTS 的簡要語法就變得相形見絀了。

使用 BINLOG 恢復資料

恢復的大致流程如下:

  1. 會建立資料庫和表,並插入資料。
  2. 誤刪一條資料。
  3. 繼續插入資料。
  4. 誤刪表。
  5. 最後將原來以及之後插入的資料進行恢復。

準備資料

準備資料庫,表及資料:

# 建立臨時資料庫
CREATE DATABASE IF NOT EXISTS test_binlog default charset utf8 COLLATE utf8_general_ci; 


# 建立臨時表
CREATE TABLE `sync_test` (`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;


# 新增資料
insert into sync_test (id, name) values (null, 'xiaoa');
insert into sync_test (id, name) values (null, 'xiaob');
insert into sync_test (id, name) values (null, 'xiaoc');

# 檢視新增的資料
select * from sync_test;

刪除表或者資料

誤刪操作:

# 刪除 name=xiaob 的資料
delete from sync_test where id=3

# 插入幾條資料
insert into sync_test (id, name) values (null, 'xiaod');
insert into sync_test (id, name) values (null, 'xiaoe');
insert into sync_test (id, name) values (null, 'xiaof');

# 刪除表
DROP TABLE sync_test;

資料的恢復

在執行資料恢復前,如果操作的是生產環境,會有如下的建議:

  • 使用 flush logs 命令,替換當前主庫中正在使用的 binlog 檔案,好處如下:
    • 可將誤刪操作,定位在一個 BINLOG 檔案中,便於之後的資料分析和恢復。
    • 避免操作正在被使用的 BINLOG 檔案,防止發生意外情況。
  • 資料的恢復不要在生產庫中執行,先在臨時庫恢復,確認無誤後,再倒回生產庫。防止對資料的二次傷害。

通常來說,恢復主要有兩個步驟:

  1. 在臨時庫中,恢復定期執行的全量備份資料。
  2. 然後基於全量備份的資料點,通過 BINLOG 來恢復誤操作和正常的資料。

使用 BINLOG 做資料恢復前:

# 檢視正在使用的 Binlog 檔案
show master status\G;
# 顯示結果是: mysql-bin.000034

# 執行 flush logs 操作,生成新的 BINLOG
flush logs;

# 檢視正在使用的 Binlog 檔案
show master status\G;
# 結果是:mysql-bin.000035

確定恢復資料的步驟:

這裡主要是有兩條誤刪的操作,資料行的誤刪和表的誤刪。有兩種方式進行恢復。

  • 方式一:首先恢復到刪除表操作之前的位置,然後再單獨恢復誤刪的資料行。
  • 方式二:首先恢復到誤刪資料行的之前的位置,然後跳過誤刪事件再恢復資料表操作之前的位置。

這裡採用方式一的方案進行演示,由於是演示,就不額外找一個臨時庫進行全量恢復了,直接進行操作。

查詢建立表的事件位置和刪除表的事件位置

#  根據時間確定位置資訊
mysqlbinlog --no-defaults --base64-output=decode-rows -v  --start-datetime  "2019-11-22 14:00:00" --database test_binlog  mysql-bin.000034 | less

建立表的開始位置:

刪除表的結束位置:

插入 name='xiaob' 的位置:

# 根據位置匯出 SQL 檔案
mysqlbinlog --no-defaults --base64-output=decode-rows -v --start-position "2508132" --stop-position "2511004" --database test_binlog  mysql-bin.000034 > /home/mysql_backup/test_binlog_step1.sql
 
 
mysqlbinlog --no-defaults --base64-output=decode-rows -v --start-position "2508813" --stop-position "2509187" --database test_binlog  mysql-bin.000034 > /home/mysql_backup/test_binlog_step2.sql
 

# 使用 mysql 進行恢復
mysql -u cisco -p < /home/mysql_backup/test_binlog_step1.sql
mysql -u cisco -p < /home/mysql_backup/test_binlog_step2.sql

MySQL 5.7 中無論是否開啟 GTID 的配置,在每次事務開啟時,都首先會出 GTID 的一個事務,用於並行複製。所以在確定匯出開始事務位置時,要算上這個事件。

在使用 --stop-position 匯出時,會匯出在指定位置的前一個事件,所以這裡要推後一個事務。

對於 DML 的語句,主要結束位置要算上 COMMIT 的位置。

總結

在文章開始時,我們熟悉了操作 BINLOG 的兩種方式 CLI 和 mysqlbinlog 工具,接著介紹了其間的區別和使用場景,對於一些大型的 BINLOG 檔案,使用 mysqlbinlog 會更加的方便和效率。並對 mysqlbinlog 的一些常見引數進行了介紹。

接著通過使用 mysqlbinlog 實際模擬了資料恢復的過程,並在恢復資料時,提出了一些需要注意的事項,比如 flush logs 等。

最後在恢復資料時,要注意 start-positionend-position 的一些小細節,來保證找到合適的位置。

參考

point-in-time-recovery

recovery-from-backups

backup-poli