日常開發,運維中,經常會出現誤刪數據的情況。誤刪數據的類型大致可分為以下幾類:
- 使用 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
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 恢復數據
恢復的大致流程如下:
- 會創建數據庫和表,並插入數據。
- 誤刪一條數據。
- 繼續插入數據。
- 誤刪表。
- 最后將原來以及之后插入的數據進行恢復。
准備數據
准備數據庫,表及數據:
# 創建臨時數據庫
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 文件,防止發生意外情況。
-
數據的恢復不要在生產庫中執行,先在臨時庫恢復,確認無誤后,再倒回生產庫。防止對數據的二次傷害。
通常來說,恢復主要有兩個步驟:
- 在臨時庫中,恢復定期執行的全量備份數據。
- 然后基於全量備份的數據點,通過 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-position
和 end-position
的一些小細節,來保證找到合適的位置。