MySQL binlog 使用場景

一. 基礎使用場景

## 直接解析binlog文件內容
shell> mysqlbinlog mysql-bin.000001 |less

## 顯示更詳細的binlog信息
## binlog_rows_query_log_events爲ON時,需要使用-vv纔可看到具體SQL
shell> mysqlbinlog -vv mysql-bin.000001 |less

## 只查看指定GTID事務記錄
shell> mysqlbinlog --include-gtids='e165cded-5c97-11e8-9814-06d1a5b64aec:2'  mysql-bin.000001 |less

關於--exclude-gtids的用法

  • 排除單條:'8528c8ee-1284-11e9-9e33-02000aba3c10:525'
  • 排除連續:'8528c8ee-1284-11e9-9e33-02000aba3c10:525-526'
  • 排除不連續多條:'8528c8ee-1284-11e9-9e33-02000aba3c10:525-526,8528c8ee-1284-11e9-9e33-02000aba3c10:528'

關於--stop-datetime指定時間點的建議: 假如在2019-01-01 21:00:00誤操作進行了數據庫刪除,則指定--stop-datetime參數時可以直接指定到2019-01-01 21:00:00,或者更加嚴謹來說,通過定位binlog中該誤操作以及該誤操作上一個事務的時間點來決定恢復時的最後時間。如以下binlog數據,則指定stop-datetime時只需保持時間在(2019-01-01 20:59:01,2019-01-01 21:00:00]內即可

  • 2019-01-01 20:40:01 INSERT INTO XXX
  • 2019-01-01 20:48:01 UPDATE INTO XXX
  • 2019-01-01 20:50:01 DELETE INTO XXX
  • 2019-01-01 20:59:01 DELETE INTO XXX
  • 2019-01-01 21:00:00 DROP DATABASE XXX

二. 統計binlog中各表的[增刪改]次數

mysqlbinlog  --no-defaults \
--base64-output=decode-rows  \
-vv mysql-bin.000032 | awk '/###/ {if($0~/UPDATE|INSERT|DELETE/)count[$2" "$NF]++}END{for(i in count) print i,"\t",count[i]}' | column -t | sort -k2nr

輸出示例

三. 解析定位binlog中大事務

備註:該命令由同事編寫

## 結果將輸出大事務的大小及起始點位置
mysqlbinlog mysql-bin.000001 | grep "last_committed=" -B 1| awk '/^# at/&&NR==1 {tmp=$NF} /^# at/&&NR>1 {print($NF-tmp,"--start-position="tmp,"--stop-position="$NF);tmp=$NF}' | sort -n -r | head -n 20

## 直接指定大事務的起始點位置查看事務具體內容
mysqlbinlog -vv --start-position=46746293 --stop-position=938267951 mysql-bin.000001|less

## 也可在定位到具體的大事務GTID後單獨解析判斷這個大事務涉及的記錄數
mysqlbinlog -vv --include-gtids='56929ffe-5d09-11ea-bb4e-02000aba3da2:42312' mysql-bin.000001|egrep "### UPDATE|### DELETE|### INSERT"|wc -l

輸出示例

四. 多維度解析binlog

備註:命令摘取自網絡

基本邏輯: 先用腳本將binlog的結果解析彙總,再基於彙總的數據做過濾排序彙總

vim analyze_binlog.sh

#!/bin/bash
## 給定binlog目錄及文件名稱
BINLOG_DIR=/data/mysql/data
BINLOG_FILE="mysql-bin.000001"

## 具體解析命令
${BINLOG_DIR}/${BINLOG_FILE} | awk \
mysqlbinlog --base64-output=decode-rows -vv ${BINLOG_DIR}/${BINLOG_FILE} | awk \
'BEGIN {s_type=""; s_count=0;count=0;insert_count=0;update_count=0;delete_count=0;flag=0;} \
{if(match($0, /#.*Table_map:.*mapped to number/)) {printf "Timestamp : " $1 " " $2 " Table : " $(NF-4); flag=1} \
else if (match($0, /(### INSERT INTO .*..*)/)) {count=count+1;insert_count=insert_count+1;s_type="INSERT"; s_count=s_count+1;}  \
else if (match($0, /(### UPDATE .*..*)/)) {count=count+1;update_count=update_count+1;s_type="UPDATE"; s_count=s_count+1;} \
else if (match($0, /(### DELETE FROM .*..*)/)) {count=count+1;delete_count=delete_count+1;s_type="DELETE"; s_count=s_count+1;}  \
else if (match($0, /^(# at) /) && flag==1 && s_count>0) {print " Query Type : "s_type " " s_count " row(s) affected" ;s_type=""; s_count=0; }  \
else if (match($0, /^(COMMIT)/)) {print "[Transaction total : " count " Insert(s) : " insert_count " Update(s) : " update_count " Delete(s) : " \
delete_count "] \n+----------------------+----------------------+----------------------+----------------------+"; \
count=0;insert_count=0;update_count=0; delete_count=0;s_type=""; s_count=0; flag=0} } '

## 執行方式
sh analyze_binlog.sh >> analyze_binlog.log
  • 輸出樣式

1.查看該binlog中 操作最多的表

cat analyze_binlog.log | grep Table |cut -d':' -f5| cut -d' ' -f2 | sort | uniq -c | sort -nr

2.查看該binlog中 刪除操作最多的表

cat analyze_binlog.log |grep -E 'DELETE' |cut -d':' -f5| cut -d' ' -f2 | sort | uniq -c | sort -nr

3.查看該binlog中 指定庫表的操作統計

cat analyze_binlog.log |grep -i '`demo`.`t1`' | awk '{print $7 " " $11}' | sort -k1,2 | uniq -c

4.查看該binlog中 單個操作影響行數的top 3

cat analyze_binlog.log | grep Table | sort -nr -k 12 | head -n 3

5.查看該binlog中 刪除超過1000行的操作

cat analyze_binlog.log |grep -E 'DELETE' | awk '{if($12>1000) print $0}'

6.查看該binlog中 所有類型操作超過1000行的操作

cat analyze_binlog.log |grep -E 'Table' | awk '{if($12>1000) print $0}'

五. MySQL Binlog Server

1.獲取遠程數據庫binlog日誌

  • --read-from-remote-server 從遠端服務器獲取binlog到本地
  • --raw 指定以binlog日誌原始格式轉儲
  • --to-last-log 從指定binlog日誌開始直到獲取到最新的binlog結束
## 獲取單個文件
mysql -h47.97.110.54 -uapp -papp -P3306 -e "show binary logs"
mysqlbinlog --raw --read-from-remote-server -h47.97.110.54 -uapp -papp -P3306 mysql-bin-zhenxing.000001>mysql-bin-zhenxing.000001

## 從指定文件獲取到最後一個文件
mysqlbinlog --read-from-remote-server -h47.97.110.54 -uapp -papp -P3306 --to-last-log mysql-bin-zhenxing.000001 --raw

2.模擬複製線程實現Binlog Server

  • --raw 指定以binlog日誌原始格式轉儲
  • --result-file 增加轉儲的binlog文件名前綴,也可指定具體目錄
  • --stop-never 指定持續轉儲binlog日誌
  • --stop-never-slave-server-id 默認65535,顯式指定可避免與其他dump線程衝突
mysqlbinlog --raw --read-from-remote-server -h47.97.110.54 -urepl -prepl -P3306 --result-file=remote- mysql-bin-zhenxing.000001 --stop-never --stop-never-slave-server-id=100 &
  • 侷限性
    • 數據庫重啓後,需重新開啓mysqlbinlog 轉儲線程並指定新的binlog日誌轉儲起始點
    • mysqlbinlog 轉儲線程異常斷開後,服務端dump線程不會消失而一直處於僵死狀態
      • 如果mysqlbinlog轉儲線程重新以相同配置啓動,則僵死進程會被激活再次使用
    • 指定-raw選項以binlog日誌原始格式轉儲時,最後一個事務始終會被標記爲rollback狀態,導致最後一個事務丟失(MySQL5.7.22未復現該問題,可能已被修復)

六. binlog備份恢復-基於複製特性

1. 使用mysqlbinlog恢復的劣勢

通常我們在數據庫誤操作,如drop database後需要基於物理備份及binlog實現數據的恢復,常規的做法如下

  1. 恢復全量備份數據
  2. 恢復增量備份數據(如有)
  3. 基於備份記錄的日誌位點或GTID恢復binlog數據到誤操作刪除之前

使用mysqlbinlog解析還原binlog過程存在以下問題

  1. 存在大量binlog時,效率過低
  2. 一旦解析異常,不便於做斷點同步
  3. 跨binlog解析存在一些限制(如創建臨時表操作)
  4. 對於單庫或單表的還原,無法做到精細化控制

2. 使用SQL線程回放binlog的優勢

基於以上直接用mysqlbinlog解析日誌會帶來的問題,結合MySQL複製同步特性,我們知道relay log本質上就是binlog,那我們可以嘗試將binlog僞裝成relay log,用複製的SQL線程來回放binlog,從而達到高效恢復的目的,且由於用到SQL線程回放,也就能使用到回放的特性,如並行回放,庫表過濾等等

優勢點基本就是MySQL複製的基本特點,有如下優勢

  1. 只需配置好同步的位點,數據庫自動使用SQL線程恢復數據
  2. 可以使用MySQL並行複製的特性,提升恢復效率
  3. 可以精細化控制恢復的庫表
  4. 可以指定恢復到具體的GTID跳過誤操作的GTID
  5. 數據恢復可以暫停,也可重新基於當前位置繼續同步

3. 恢復的場景示例

以下是使用SQL線程回放binlog的故障場景模擬信息

  1. 做全量xtrabackup備份模擬日常備份
  2. 執行sysbench壓測4張表,20個線程,壓測10分鐘,模擬大量binlog
  3. 刪除實例模擬數據庫被誤刪除或硬件故障(binlog需要保留)
  4. 使用xtrabackup恢復全量備份
  5. 使用MySQL Replication SQL線程回放binlog(注意:恢復前需要將relay_log_recocery參數設置爲0

4. 環境準備及故障模擬

4.1 binlog Server備份模擬

使用mysqlbinlog將binlog文件實時轉儲到備份目錄,模擬binlog備份(當前環境備份目錄配置在/data/mysql/backup),該步驟可以改爲其他binlog備份方式如定期cp或rsync或不備份也可

[root@10-186-61-162 ~]# mkdir -p /data/mysql/backup/binlog
[root@10-186-61-162 ~]# mysqlbinlog --raw --read-from-remote-server -h10.186.61.162 -urepl -psyeWVv1jnWImRaQD -P3306 --result-file=/data/mysql/backup/binlog/ mysql-bin.000001 --stop-never --stop-never-slave-server-id=100 &

4.2 生成模擬數據並模擬壓力

生成sysbench測試數據,創建4張表,每張各100W數據庫並壓測一分鐘模擬數據變化

## 插入基礎數據
[root@10-186-61-162 ~]# sysbench /usr/local/share/sysbench/oltp_read_write.lua --db-ps-mode=disable --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=sysbench --mysql-password=sysbench --mysql-db=sbtest --tables=4 --table-size=1000000 --report-interval=1 --time=600 --threads=20  prepare
  
## 執行sysbench壓測模擬數據變化,壓測1分鐘
[root@10-186-61-162 ~]# sysbench /usr/local/share/sysbench/oltp_read_write.lua --db-ps-mode=disable --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=sysbench --mysql-password=sysbench --mysql-db=sbtest --tables=4 --table-size=1000000 --report-interval=1 --time=60 --threads=20  run

4.3 模擬每天物理備份

執行物理備份模擬每天的全量備份

[root@10-186-61-162 ~]# innobackupex --host=10.186.61.162 --port=3306 --user=backup --password=87hcvjYnJPT4k9tr /data/mysql/backup/

4.4 模擬備份後的數據變更

[root@10-186-61-162 ~]# sysbench /usr/local/share/sysbench/oltp_read_write.lua --db-ps-mode=disable --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=sysbench --mysql-password=sysbench --mysql-db=sbtest --tables=4 --table-size=1000000 --report-interval=1 --time=600 --threads=20  run

4.5 觸發誤操作模擬故障

## 生成一個新的binlog包含誤操作測試環境爲了方便查找binlog位點
flush logs

## 執行誤操作刪庫
drop database sbtest;

5. 數據還原操作步驟

5.1 還原全量備份

[root@10-186-61-162 ~]# cd /data/mysql/backup
[root@10-186-61-162 backup]# innobackupex --apply-log 2020-03-02_17-39-07/
[root@10-186-61-162 backup]# innobackupex --defaults-file=/etc/my.cnf --copy-back 2020-03-02_17-39-07/

5.2 binlog轉relay log

將binlog轉換爲relay log並拷貝到數據目錄以便SQL線程回放數據

[root@10-186-61-162 backup]# cd /data/mysql/backup/binlog/
  
## 將binlog重命名爲relay log
[root@10-186-61-162 binlog]# rename mysql-bin mysql-relay mysql-bin*
  
## 創建relay log index文件
[root@10-186-61-162 binlog]# ls ./mysql-relay.0000* >mysql-relay.index
  
## 拷貝relay log到數據目錄
[root@10-186-61-162 binlog]# cp mysql-relay.* /data/mysql/data/
  
## 修改數據目錄權限
[root@10-186-61-162 binlog]# chown -R mysql:mysql /data/mysql/data/

5.3 relay_log_recovery參數配置

修改relay_log_recovery參數,設置爲0並啓動數據庫,relay_log_recovery爲1時,relay log會在複製線程啓動時被清除重新拉

## 獲取備份文件中的binlog位點信息及GTID點對應的relay log文件
[root@10-186-61-162 ~]# cd /data/mysql/backup/2020-03-02_17-39-07/
[root@10-186-61-162 2020-03-02_17-39-07]# cat xtrabackup_binlog_info
mysql-bin.000007    80456866    f41abe78-5c62-11ea-abf1-02000aba3da2:1-52013
  
## 啓動數據庫
[root@10-186-61-162 2020-03-02_17-39-07]# systemctl start mysql_3306
  
## change master指定一個空的主庫,創建SQL線程
root@localhost[(none)]> reset master;
  
## 指定備份文件中對應的binlog位點
## 其中MASTER_HOST任意指定一個不存在的地址即可,RELAY_LOG_FILE和RELAY_LOG_POS填入全量備份中的值
root@localhost[(none)]> CHANGE MASTER TO MASTER_HOST='1.1.1.1',RELAY_LOG_FILE='mysql-relay.000007',RELAY_LOG_POS=80456866;
  
## 查看指定的位點是否生效
root@localhost[(none)]> select * from mysql.slave_relay_log_info;
+-----------------+----------------------+---------------+-----------------+----------------+-----------+-------------------+----+--------------+
| Number_of_lines | Relay_log_name       | Relay_log_pos | Master_log_name | Master_log_pos | Sql_delay | Number_of_workers | Id | Channel_name |
+-----------------+----------------------+---------------+-----------------+----------------+-----------+-------------------+----+--------------+
|               7 | ./mysql-relay.000007 |      80456866 |                 |              0 |         0 |                 0 |  1 |              |
+-----------------+----------------------+---------------+-----------------+----------------+-----------+-------------------+----+--------------+
  
## 只需要開啓SQL線程對指定的relay log開始回放即可
root@localhost[(none)]> START SLAVE SQL_THREAD;

## 持續執行可看到binlog數據開始回放
root@localhost[(none)]> show slave status\G
  
## 待binlog恢復完成且數據校驗無問題後可將複製關係重置
root@localhost[(none)]> stop slave;
root@localhost[(none)]> reset slave;
root@localhost[(none)]> reset slave all;

6. 數據還原精細化控制

6.1 只恢復單個庫的數據

由於備份是全量備份,無法單庫還原(如果已知表結構可使用可傳輸表空間方式單庫還原),可先將全量恢復後對只對單庫做binlog還原
只需在數據恢復步驟的基礎上,開啓SQL線程回放操作前增加一條以下命令即可實現對sbtest單庫的binlog還原

  • CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE = ('sbtest.%');

6.2 只恢復單個表的數據

該步驟與單庫還原類似,只需將複製過濾屬性指定爲具體的表,如
- CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE = ('sbtest.sbtest1','sbtest.sbtest2');
可在開啓binlog還原前後對sbtest1-4表做checksum,驗證是否只有sbtest1和sbtest2做了binlog還原
checksum table sbtest1,sbtest2,sbtest3,sbtest4;

6.3 恢復到指定的GTID或position點

該步驟只需在數據恢復步驟的基礎上,將START SLAVE SQL_THREAD改爲START SLAVE SQL_THREAD UNTIL SQL_BEFORE_GTIDS,如只恢復到GTID=499999

  • START SLAVE SQL_THREAD UNTIL SQL_BEFORE_GTIDS = 'f41abe78-5c62-11ea-abf1-02000aba3da2:500000;

6.4 提升恢復效率的參數優化

set global sync_binlog=0; 					  ## 也可直接關閉binlog
set global innodb_flush_log_at_trx_commit=0;  ## 8.0支持關閉redo log
set global slave_parallel_type=LOGICAL_CLOCK;
set global slave_parallel_workers=8;

6.5 mysqlbinlog與SQL線程回放效率對比

恢復方式 binlog數量 恢復耗時 備註
mysqlbinlog 1.2G 722s sync_binlog=0和innodb_flush_log_at_trx_commit=0
SQL_THREAD 1.2G 151s sync_binlog=0和innodb_flush_log_at_trx_commit=0,且並行複製設置爲8,實際僅使用2個線程回放
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章