本文介紹binlog的作用以及幾個重要參數的使用方法,同時通過實驗來描述binlog內部記錄內容:row 、statement跟mixed的設置下,記錄了哪些東西,最后會簡單介紹下binlog server的搭建以及一些關於binlog使用的小Tips。
理解跟熟悉binlog相關內容,對復制原理及故障處理會有很大幫助的。
如果轉載,請注明博文來源:
www.cnblogs.com/xinysu/ ,版權歸 博客園內 蘇家小蘿卜 所有。望各位支持!
1 what's binary log
Binary log 用來記錄數據庫中發生的修改情況,比如數據的修改、表格的創建及修改等,它既可以記錄涉及修改的SQL,也可以記錄數據修改的行變化記錄,同時也記錄了執行時間。比如,執行sql:update tabname set cola='a' where id between 1 and 5,修改了5行記錄。當開啟binlog記錄的時候,根據設置的binlog格式,可能記錄的是這一條SQL語句,也可能記錄的是5行數據記錄的修改情況,也可能兩者都有,這部分詳情可以看本博文的第3部分:binlog formats。
這里注意跟general log區分下,binnary log是記錄數據庫內部的修改情況,而general log是記錄所有數據庫的SQL操作情況,比如像select或者show這種語句,不會發生數據修改,則不會記錄到binnary log,但是屬於數據庫操作記錄,會記錄到general log。
那么,開啟它,有什么好處,有什么確定呢 ?
首先,好處有3個:
- 搭建復制架構的時候,需要binary log 來記錄數據庫的修改event;
- 數據庫宕機恢復使用;
- 異常操作,緊急恢復數據使用;
那么,當開啟binlog記錄日志的時候,也就以為着有一定的IO量被占用,相對而言,數據庫會比不開啟的時候稍微慢些。但是由於帶來的好處比較多且重要,這點性能影響在大多數情況下可以忽略。
2 Binary Logging Options and Variables
2.1 基礎參數
- 文件大小
- max_binlog_size
- 范圍4k-1G,默認為1G;這里注意下,並非設置了 max_binlog_size=1G,binlog文件最大就為1G,當事務短且小的情況下,binlog接近1G的時候,就會flush log,生成新的binlog文件,但是,但是,但是,但是同個事務是不能夠跨多個binlog文件存儲,一個事務只能存儲在一個binlog文件。如果這個時候,有個大事務,假設單個SQL UPDATE了100w行數據,SQL產生的binlog日志記錄有5G,那么當前的binlog文件則會出現大於5G的情況,該事務結束后,才會切換binlog文件。
- max_binlog_size
- 緩存大小
- binlog_cache_size
- binlog寫緩沖區設置大小,由於是內存,寫速度非常快,可以有效提高binlog的寫效率,如果數據庫中經常出現大事務,可以酌情提高該參數。
- 那么,如果觀察自家DB實例的binlog_cache_size設置是否合理呢?可以通過show status like 'Binlog_cache%';查看Binlog_cache_use and Binlog_cache_disk_use的使用情況,Binlog_cache_use表示用到binlog緩沖區的次數,Binlog_cache_disk_use ,使用臨時文件來存放binlog cache的次數,如果Binlog_cache_disk_use的次數過多,可以酌情提高該參數。詳見下圖。
- binlog_stmt_cache_size
- 保留一個事務內,非事務語句文本的緩存大小。默認32k。
- 與binlog_cache_size一樣,也可以通過show status like 'binlog_stmt_cache%'來查看是否設置合理。查看參數為:Binlog_stmt_cache_use (使用緩存區的次數),Binlog_stmt_cache_disk_use(使用臨時文件的次數)
- max_binlog_cache_size
- 默認為4G,如果發生大事務占用binlog cache超過設置值,則會報錯 : multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage。
- 這時候,就有個疑問了,為啥存在了 binlog_cache_size的設置,還需要 max_binlog_cache_size呢?
- 其實是這樣,當一個線程連接進來並開始執行事務的時候,數據庫會按照binlog_cache_size的大小分配給它一個緩沖區域,如果使用到的空間要大於binlog_cache_size,則會使用臨時文件來存儲,線程結束后再刪除臨時文件。
- 而max_binlog_cache_size則是嚴格限制了一個多SQL事務總的使用binlog cache的大小,保留分配緩沖區域跟臨時文件,總大小不能超過max_binlog_cache_size的限制值,一旦超過,則會報錯multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage。
- max_binlog_stmt_cache_size
- 默認4G。超過則報錯。注意事項跟 max_binlog_cache_size 類似。
- binlog_cache_size
- binlog文件相關
- log_bin_basename
- binlog文件的命名方式
- log_bin_index
- binlog索引文件的絕對路徑
- expire_logs_days
- binlog保留的有效天數,過期的會自動刪除
- 這里有個小tips,假設當前binlog文件過多且大占用磁盤空間,可以修改小改參數,改參數只有在切換新的binlog文件時,才會刪除過期文件,也就是可以等數據庫把當前binlog寫滿后切換到新文件的時候刪除,也可以手動執行flush logs,手動切換binlog,同時會觸發對過期binlog文件的刪除。
- log_bin_basename
2.2 重要參數(sync_binlog=0丟失數據的描述有疑問,目前查閱相關資料跟咨詢業界人士中....)
- binlog開關
- log_bin
- 需要在數據庫配置文件中添加或者指定--log-bin=[base-name]啟動DB服務,重啟后修改才生效
- 需要在數據庫配置文件中添加或者指定--log-bin=[base-name]啟動DB服務,重啟后修改才生效
- log_bin
- 日志記錄內容相關
- binlog_format
- 多么重要的參數,以至於本文開了一節來細講,詳見 第三部分
- 設置binlog的記錄格式
- 5.7.6前默認statement,5.7.7后默認row,可選row,mixed,statement
- binlog_row_image
- 主要針對當binlog_format=row格式 下的設置,
- 默認full,可選full,minimal,noblob
- binlog_rows_query_log_events
- 主要針對當binlog_format=row格式 下的設置,如果基於row記錄binlog日志,默認是只記錄變化的行數據,不記錄涉及執行的SQL語句,如果開啟此參數,則會一同記錄執行的SQL語句
- 默認false
- binlog_gtid_simple_recovery
- GTID復制會使用到,該參數控制 配置了的GTID復制到實例,在重啟時或者清理binlog文件時,數據庫只需要打開最老跟最新兩個binlog文件取出gtid_purged and gtid_executed,不需要打開所有文件
- 默認為false,這個參數是社區反饋給官方添加,調整這個選項設置為True,對性能會有所提高,但是在某些環境下,由於只打開兩個文件來計算,所以計算gtids值可能會出錯。而保持這個選項值為false,能確保計算總是正確。
- 組提交(提高binary log並發提交的數據量)
- binlog_group_commit_sync_delay
- 默認為0
- 結合binlog_group_commit_sync_no_delay_count來理解,見下文
- binlog_group_commit_sync_no_delay_count
- 默認為0
- MySQL等待binlog_group_commit_sync_delay毫秒的時間直到 binlog_group_commit_sync_no_delay_count個數時進行一次組提交,如果binlog_group_commit_sync_delay毫秒內也還沒有到達指定的個數,也會提交。
- flush disk相關
- sync_binlog
- 5.7.7前默認為0,之后默認為1,范圍0-4294967295
- binlog_format
-
-
- sync_binlog =0,則是依賴操作系統刷新文件的機制,MySQL不會主動同步binlog內容到磁盤文件中去,而是依賴操作系統來刷新binary log。
-
-
-
- sync_binlog =N (N>0) ,則是MySQL 在每寫 N次 二進制日志binary log時,會使用fdatasync()函數將它的寫二進制日志binary log同步到磁盤中去。
-
-
-
- 注: 如果啟用了autocommit,那么每一個語句statement就會有一次寫操作;否則每個事務對應一個寫操作。
-
-
-
- 如果設置sync_binlog =0 ,發生crash事件(服務器),數據庫最高丟失binlog內容為1s內寫在file system buffer的內容;
-
-
-
- 如果設置sync_binlog =N ,發生crash事件(服務器),數據庫最高丟失binlog內容為寫在file system buffer內 N個binlog events;
- 如果設置sync_binlog =N ,發生crash事件(服務器),數據庫最高丟失binlog內容為寫在file system buffer內 N個binlog events;
-
-
-
- 這個參數經常跟innodb_flush_log_at_trx_commit結合調整,提高性能或者提高安全性(詳細可查看上周博文:http://www.cnblogs.com/xinysu/p/6555082.html 中 “redo參數” 一節),這里提2個推薦的配置:
-
-
-
-
- innodb_flush_log_at_trx_commit和sync_binlog 都為 1(俗稱雙一模式),在mysqld 服務崩潰或者服務器主機crash的情況下,binary log 只有可能丟失最多一個語句或者一個事務。但是有得必有舍,這個設置是最安全但也是最慢的。適合數據一致性要求較高,核心業務使用。
-
-
-
-
-
- innodb_flush_log_at_trx_commit=2 ,sync_binlog=N (N為500 或1000) ,但是但是但是,服務器一定要待用蓄電池后備電源來緩存cache,在服務器crash后,還能支持把file system buffer中的內容寫入到binlog file中,防止系統斷電異常。這種適合當磁盤IO無法滿足業務需求時,比如節假日或者周年活動產生的數據庫IO壓力,則推薦這么設置。
-
-
3 Binary Logging Formats
這一部分,將通過實驗來說明。我們會使用到mysqlbinlog指令,其具體用法詳見:
https://dev.mysql.com/doc/refman/5.7/en/mysqlbinlog.html 。
還記得你剛剛看到“日志記錄內容相關 ” 小節里那三個紅燦燦喜洋洋的參數嗎?哈哈哈,見下文:
- binlog_format
- 多么重要的參數,以至於本文開了一節來細講,詳見 第三部分
- 設置binlog的記錄格式
- 5.7.6前默認statement,5.7.7后默認row,可選row,mixed,statement
- binlog_row_image
- 主要針對當binlog_format=row格式 下的設置,
- 默認full,可選full,minimal,noblob
- binlog_rows_query_log_events
- 主要針對當binlog_format=row格式 下的設置,如果基於row記錄binlog日志,默認是只記錄變化的行數據,不記錄涉及執行的SQL語句,如果開啟此參數,則會一同記錄執行的SQL語句
- 默認false
實驗內容:
- 設置binlog format格式;設置隔離級別;
- 創建表格
- INSERT操作(重點查看UUID()函數使用情況)
- UPDATE操作(檢查自動更新時間列)
- DELETE操作
3.1 binlog_format=statement
1 #測試前環境准備及清理: 2 mysql> set binlog_format='statement'; 3 mysql> SET session tx_isolation='REPEATABLE-READ'; 4 mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation; 5 mysql> show variables like 'binlog_format' ; 6 mysql> flush logs; 7 mysql> show master status;
測試前環境准備及清理:
模擬DDL操作及DML操作:

DDL跟DML模擬結束后,得到當前的binlog文件是 ,結束的position是,所以直接讀取整個文件從position=154到i2216之間的操作記錄,使用mysqlbinlog讀取。
[root@localhost ~]# /usr/local/mysql/bin/mysqlbinlog --start-position=154 --stop-position=2216

[root@localhost ~]# /usr/local/mysql/bin/mysqlbinlog --start-position=154 --stop-position=2216 /data/mysql/mysql3306/logs/bin_log.000016 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #170323 15:29:32 server id 1793306 end_log_pos 123 CRC32 0xea8ce874 Start: binlog v 4, server v 5.7.14-log created 170323 15:29:32 # Warning: this binlog is either in use or was not closed properly. BINLOG ' XHnTWA8aXRsAdwAAAHsAAAABAAQANS43LjE0LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA AXTojOo= '/*!*/; # at 154 #170323 15:29:40 server id 1793306 end_log_pos 219 CRC32 0x5b603be7 Anonymous_GTID last_committed=0 sequence_number=1 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 219 #170323 15:29:40 server id 1793306 end_log_pos 627 CRC32 0xa60f096b Query thread_id=37 exec_time=0 error_code=0 use `binlogdb`/*!*/; SET TIMESTAMP=1490254180/*!*/; SET @@session.pseudo_thread_id=37/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=1436549152/*!*/; SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/; /*!\C utf8 *//*!*/; SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; SET @@session.explicit_defaults_for_timestamp=1/*!*/; CREATE TABLE `tbstatement` ( `id` int(11) NOT NULL AUTO_INCREMENT, `rowformat` varchar(50) NOT NULL, `uuids` varchar(50) NOT NULL, `timepoint` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, CurrentVersion timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB /*!*/; # at 627 #170323 15:29:45 server id 1793306 end_log_pos 692 CRC32 0x91e4c6df Anonymous_GTID last_committed=1 sequence_number=2 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 692 #170323 15:29:45 server id 1793306 end_log_pos 787 CRC32 0xf21ab215 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; SET @@session.time_zone='+08:00'/*!*/; BEGIN /*!*/; # at 787 # at 819 #170323 15:29:45 server id 1793306 end_log_pos 819 CRC32 0xdfd757e2 Intvar SET INSERT_ID=1/*!*/; #170323 15:29:45 server id 1793306 end_log_pos 978 CRC32 0x5cd9e755 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; insert into tbstatement(rowformat,uuids) select 'tbstatement1',uuid() /*!*/; # at 978 #170323 15:29:45 server id 1793306 end_log_pos 1009 CRC32 0x212a23f8 Xid = 285 COMMIT/*!*/; # at 1009 #170323 15:29:45 server id 1793306 end_log_pos 1074 CRC32 0x8c4123f6 Anonymous_GTID last_committed=2 sequence_number=3 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 1074 #170323 15:29:45 server id 1793306 end_log_pos 1169 CRC32 0x9ac5a016 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; BEGIN /*!*/; # at 1169 # at 1201 #170323 15:29:45 server id 1793306 end_log_pos 1201 CRC32 0xd294bbeb Intvar SET INSERT_ID=2/*!*/; #170323 15:29:45 server id 1793306 end_log_pos 1359 CRC32 0x2fdbd77b Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; insert into tbstatement(rowformat,uuids) select 'tbstatement',uuid() /*!*/; # at 1359 #170323 15:29:45 server id 1793306 end_log_pos 1390 CRC32 0xbc159cbe Xid = 286 COMMIT/*!*/; # at 1390 #170323 15:29:45 server id 1793306 end_log_pos 1455 CRC32 0x08e548c6 Anonymous_GTID last_committed=3 sequence_number=4 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 1455 #170323 15:29:45 server id 1793306 end_log_pos 1542 CRC32 0xfee000eb Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; BEGIN /*!*/; # at 1542 #170323 15:29:45 server id 1793306 end_log_pos 1689 CRC32 0x37403367 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; update tbstatement set rowformat='tbstatement1_update' where id=1 /*!*/; # at 1689 #170323 15:29:45 server id 1793306 end_log_pos 1720 CRC32 0xcd22def6 Xid = 288 COMMIT/*!*/; # at 1720 #170323 15:29:45 server id 1793306 end_log_pos 1785 CRC32 0x0ee70541 Anonymous_GTID last_committed=4 sequence_number=5 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 1785 #170323 15:29:45 server id 1793306 end_log_pos 1872 CRC32 0x84b521c4 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; BEGIN /*!*/; # at 1872 #170323 15:29:45 server id 1793306 end_log_pos 1988 CRC32 0x993a03d3 Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254185/*!*/; delete from tbstatement where id=2 /*!*/; # at 1988 #170323 15:29:45 server id 1793306 end_log_pos 2019 CRC32 0xb42df79d Xid = 289 COMMIT/*!*/; # at 2019 #170323 15:29:46 server id 1793306 end_log_pos 2084 CRC32 0xdce8d553 Anonymous_GTID last_committed=5 sequence_number=6 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/; # at 2084 #170323 15:29:46 server id 1793306 end_log_pos 2216 CRC32 0xc3c62bae Query thread_id=37 exec_time=0 error_code=0 SET TIMESTAMP=1490254186/*!*/; DROP TABLE `tbstatement` /* generated by server */ /*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ; # End of log file /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
逐個事務拆分開看如下圖:

小結:
- 當binlog_format=statement的時候,DDL及DML都是明文按照SQL記錄存儲
- 對復制的影響
- 某系統參數由於在不同時間不同服務器,執行結果不一致,這會給復制的主從帶來數據不一致的嚴重影響
- LOAD_FILE(), UUID(), USER(),FOUND_ROWS(),defaults now()及用戶自定義函數等
- 同步到從庫的binlog都是SQL語句,在slave端再跑一遍,假設一個update語句性能很差,但是最終只修改了一行數據,那么在從庫也會同樣執行這個性能差的SQL
- 而對於 insert tb select * from tbname 這類型的SQL,則只需要同步一行SQL語句即可
3.2 binlog_format=row
3.2.1 binlog_row_image默認full,binlog_rows_query_log_events默認false
1 set binlog_format='row'; 2 SET session tx_isolation='REPEATABLE-READ'; 3 SELECT @@GLOBAL.tx_isolation, @@tx_isolation; 4 show variables like 'binlog_format' ; 5 show master status; 6 flush logs; 7 show master status; 8 9 10 CREATE TABLE `tbrow` ( 11 `id` int(11) NOT NULL AUTO_INCREMENT, 12 `rowformat` varchar(50) NOT NULL, 13 `uuids` varchar(50) NOT NULL, 14 `timepoint` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 15 CurrentVersion timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 16 PRIMARY KEY (`id`) 17 ) ENGINE=InnoDB; 18 insert into tbrow(rowformat,uuids) select 'row',uuid(); 19 insert into tbrow(rowformat,uuids) select 'row',uuid(); 20 update tbrow set rowformat='tbstatement1_update' where id=1; 21 delete from tbrow where id=2; 22 DROP TABLE tbrow; 23 show master status;
找到開始與結束的position點,查看這個區間的binlog日志內容:/usr/local/mysql/bin/mysqlbinlog --start-position=154 --stop-position=2196 /data/mysql/mysql3306/logs/bin_log.000017

發現,在row格式下,DML是加密存儲,好在mysqlbinlog提供參數-v 反解析查看,指令如下:
[root@localhost ~]# /usr/local/mysql/bin/mysqlbinlog --base64-output=decode-rows -v -v --start-position=154 --stop-position=2196 /data/mysql/mysql3306/logs/bin_log.000017

小結:
- 當binlog_format=row的時候,其他參數默認,DDL明文存儲SQL腳本,DML都是加密存儲且存儲的是每一行的行記錄修改情況
- 對復制的影響
- 最安全的同步設置
- 同步到從庫的binlog都是按行記錄修改的SQL,所以假設一個update語句性能很差,但是最終只修改了一行數據,那么在從庫不需要執行這個性能差的SQL,只需要直接執行行記錄的修改結果即可(注意,使用基於row格式復制的實例,請給所有表格添加主鍵或者唯一索引,不然每一行記錄的修改都需要全表掃,會導致從庫性能非常差而且可能延時較長)
- 而對於 insert tb select * from tbname 這類型的SQL,statment格式的只需要同步一條sql,但是row的話,則需要同步所有行記錄。
3.2.2 binlog_rows_query_log_events 設置
從上小節可以看出,當binlog_format=row的時候,只記錄行修改情況,不記錄執行的SQL的。
啟動這個參數,則可在row格式下查看到執行的sql語句。
造數據中:

查看binlog日志如下:
3.2.3 binlog_row_image設置
默認 full,可選full,minimal,noblob
- full的結果可看上文的所有截圖,update的時候,set是全部列的新紀錄,where是全部的舊記錄;
- noblob
- minimal
小結
- 設置為full,則update語句的set部分是新紀錄內容,delete及update語句的where部分是全部舊記錄內容
- 設置為noblob,則update語句的set部分是新紀錄內容,delete及update語句的where部分是全部舊記錄內容,但是,如果如果修改的列不是blob或者text字段,則set部分不會出現blob及text字段;where條件中無論涉不涉及,都不會出現;
- 這個截圖表格有3列,( id int,name text, description varchar(50)),只update id為2,binlog記錄如下:
- 設置為minimal ,則update語句的set部分只有修改的列內容,delete及update語句的where部分是主鍵或者唯一索引,如果都沒有,才會使整行舊記錄。
- 這個截圖中,有5列,主鍵是id,第5列是根據行變動記錄時間,update第二列的值,第5列值自動更新,binlog記錄如下:
3.3 binlog_format=mixed
理解完statement跟row模式后,mixed混合模式就好理解了。
mixed模式下,大多數情況下,是以statement格式記錄binlog日志,當隔離級別為RC模式的時候,則修改為row模式記錄,以下幾個形式,也是以row模式記錄:
- When a function contains
UUID()
. - When one or more tables with
AUTO_INCREMENT
columns are updated and a trigger or stored function is invoked. Like all other unsafe statements, this generates a warning ifbinlog_format = STATEMENT
- When the body of a view requires row-based replication, the statement creating the view also uses it. For example, this occurs when the statement creating a view uses the
UUID()
function. - When a call to a UDF is involved.
- If a statement is logged by row and the session that executed the statement has any temporary tables, logging by row is used for all subsequent statements (except for those accessing temporary tables) until all temporary tables in use by that session are dropped
- This is true whether or not any temporary tables are actually logged.
- Temporary tables cannot be logged using row-based format; thus, once row-based logging is used, all subsequent statements using that table are unsafe. The server approximates this condition by treating all statements executed during the session as unsafe until the session no longer holds any temporary tables.
- When
FOUND_ROWS()
orROW_COUNT()
is used. (Bug #12092, Bug #30244) - When
USER()
,CURRENT_USER()
, orCURRENT_USER
is used. (Bug #28086) - When a statement refers to one or more system variables. (Bug #31168)
不如做一個小小的總結,如下圖:

4 binlog server的搭建
binlog server拿來干嘛?其實是一個實時備份binlog的配置,假設你有一台DB服務器的數據一致性及安全性非常高,你的備份策略可能是每周全備一次,每日差異備份一次,那么假設出現主從故障,需要使用備份來恢復,那么你可能丟失的數據最多可以達到一天。這個時候,其實可以搭建一個binlog server,來實時保存binlog文件在備份服務器上,它實時同步binlog文件內容,也就是當你發生宕機事故時,你可以通過備份文件+binlog server中的內容來恢復
- 搭建binlog
- 選定一台服務器,用來當做一個binlog server服務器
- 服務器上配置同個版本mysql
- 同一個binlog server 可以同步多台數據庫實例的binlog文件
- 建立每個實例對應的文件夾,cd 文件夾,進入文件夾
- /usr/local/mysql/bin/mysqlbinlog -R --raw --host=192.168.9.111 --port=3330 --user='repl' --password='replasslave' --stop-never --stop-never-slave-server-id=1113330 --start-position=14981 bin_log.000045
- /usr/local/mysql/bin/mysqlbinlog -R --raw --host=192.168.9.112 --port=3310 --user='repl' --password='replasslave' --stop-never --stop-never-slave-server-id=1443310 --start-position=154 bin_log.000044
5 Tips
5.1 修改會話級別的binlog格式
一般情況下,我們不隨意修改數據庫級別的binlog格式,因為有可能會對程序不兼容。但是當人為導數的時候,比如insert into tb select * from .. 涉及100萬行記錄的時候,如果binlog_formant為row格式,那么產生的binlog文件將非常大,而且再傳給從庫落地為relay log也很大,這占用了一定量的IO資源,這個時候,可以在操作之前,先修改當前會話級別為 set binlog_formant='statement'; 再執行insert into tb select * from ..,那么它就僅記錄這單條SQL,會話級別的binlog_format修改,不會影響整體的同步情況。
5.2 查看binlog中每個事務的開始跟結束
詳細看上面的截圖就會發現,都給大家把一個個事務畫上了框框,細心可以發現,每個事務都會有一個last_committed,如下圖。

則可以發現這個update事務從1285開始,1681結束,通過/usr/local/mysql/bin/mysqlbinlog --base64-output=decode-rows -v -v --start-position=1285 --stop-position=1681 /data/mysql/mysql3306/logs/bin_log.000017,可以正常讀取這個事務。
但是假如結尾的position讀錯,讀成1650,那么,會發生什么情況呢?
由於讀出來的事務屬於沒有提交的事務,那么mysqlbinlog則會認為這個事務需要回滾,添加rollback語句,並提示這個rollbac是由mysqlbinlog自身添加的。
/usr/local/mysql/bin/mysqlbinlog --base64-output=decode-rows -v -v --start-position=1285 --stop-position=1650 /data/mysql/mysql3306/logs/bin_log.000017
5.3 根據binlog格式恢復數據(row格式)
通過mysqlbinlog正常讀取行記錄內容,按照一個事務一個事務來反推舊記錄,insert的操作修改為delete,delete的修改為insert,update的set跟where兌換位置。
目前有不少python腳本可以用,本人也在試寫中,大家感興趣可以搜索下。
5.4 binlog文件清理(20170328修改)
- expire_logs_days+flush logs
-
- 設置expire_logs_days,假設當前binlog文件過多且大占用磁盤空間,可以修改小改參數,改參數只有在切換新的binlog文件時,才會刪除過期文件,也就是可以等數據庫把當前binlog寫滿后切換到新文件的時候刪除,也可以手動執行flush logs,手動切換binlog,同時會觸發對過期binlog文件的刪除。注意從庫的同步情況來設置。
- purge binary logs to 'xxxx'
- 清理 'xxxx' 之前的 binary log文件,只保留 'xxxx' 之后的binary log文件
- 但是使用這個指令的時候,要注意主從同步情況,如果在主庫執行,一定要去從庫 SHOW SLAVE STATUS,看下當前同步到哪個binlog文件,防止清理報錯
5.5 與binlog相關的指令
查看當前binlog文件情況:show binary logs;
查看當前使用的binlog文件及position:show master status;(復制隨處可見到使用)
查看某個文件中的binlog事件:show binlog events in 'bin_log.000003';
重置所有binlog文件,會刪除所有binlog文件哦,謹慎使用:reset master;
參考文檔: