https://yq.aliyun.com/articles/238364?spm=5176.8067842.tagmain.52.73PjU3
摘要: 概述 MySQL的安裝可以參考:Linux(CentOS)中常用軟件安裝,使用及異常——MySQL, VmTools MySQL關於Binlog的官方文檔:The Binary Log 基於Binlog的主備復制:MySQL主備復制原理、實現及異常處理 什么是 Binlog MySQL Server 有四種類型的日志——Error Log、General Query Log、Binary Log 和 Slow Query Log。
概述
MySQL的安裝可以參考:Linux(CentOS)中常用軟件安裝,使用及異常——MySQL, VmTools
MySQL關於Binlog的官方文檔:The Binary Log
基於Binlog的主備復制:MySQL主備復制原理、實現及異常處理
什么是 Binlog
MySQL Server 有四種類型的日志——Error Log、General Query Log、Binary Log 和 Slow Query Log。
第一個是錯誤日志,記錄 mysqld 的一些錯誤。第二個是一般查詢日志,記錄 mysqld 正在做的事情,比如客戶端的連接和斷開、來自客戶端每條 Sql Statement 記錄信息;如果你想准確知道客戶端到底傳了什么瞎 [嗶嗶] 玩意兒給服務端,這個日志就非常管用了,不過它非常影響性能。第四個是慢查詢日志,記錄一些查詢比較慢的 SQL 語句——這種日志非常常用,主要是給開發者調優用的。
剩下的第三種就是 Binlog 了,包含了一些事件,這些事件描述了數據庫的改動,如建表、數據改動等,也包括一些潛在改動,比如 DELETE FROM ran WHERE bing = luan,然而一條數據都沒被刪掉的這種情況。除非使用 Row-based logging,否則會包含所有改動數據的 SQL Statement。
那么 Binlog 就有了兩個重要的用途——復制和恢復。比如主從表的復制,和備份恢復什么的。
顯然,我們執行SELECT等不設計數據變更的語句是不會記錄Binlog的,而涉及到數據更新則會記錄。要注意的是,對支持事務的引擎如InnoDB而言,必須要提交了事務才會記錄Binlog。Binlog是在事務最終commit前寫入的,binlog什么時候刷新到磁盤跟參數sync_binlog相關。如果設置為0,則表示MySQL不控制binlog的刷新,由文件系統去控制它緩存的刷新,而如果設置為不為0的值則表示每sync_binlog次事務,MySQL調用文件系統的刷新操作刷新binlog到磁盤中。設為1是最安全的,在系統故障時最多丟失一個事務的更新,但是會對性能有所影響,一般情況下會設置為100或者0,犧牲一定的一致性來獲取更好的性能。
開啟和停用Binlog
通過配置/etc/my.cnf配置文件的log-bin選項:
[mysqld] log-bin=mysql-bin
這個需要重啟MySQL服務。
可以使用SET SQL_LOG_BIN=0命令停止使用日志文件,然后可以通過SET SQL_LOG_BIN=1命令來啟用。
Binlog的刪除
mysql> show variables like 'expire_log_days'; mysql> set global expire_log_days=3; //過期刪除 mysql> reset master; //刪除master的binlog mysql> reset slave; //刪除slave的中繼日志 mysql> purge master logs before '2016-10-20 16:25:00';//刪除指定日期前的日志索引中binlog日志文件 mysql> purge master logs to 'binlog.000002';//刪除指定日志文件
除了以上方法之外,也可以采用操作系統的本地命令刪除文件。
Binlog文件的擴展
當遇到以下3種情況時會重新生成一個新的日志文件,文件序號遞增:
- MySQL服務器停止或重啟時,MySQL會在重啟時生成一個新的日志文件;
- 使用flush logs命令;
- 當binlog文件大小超過max_binlog_size系統變量配置的上限時;
binlog文件的最大值和默認值是1GB,該設置並不能嚴格控制binlog的大小,尤其是binlog比較靠近最大值而又遇到一個比較大事務時,為了保證事務的完整性,不可能做切換日志的動作,只能將該事務的所有SQL都記錄到當前日志,直到事務結束。
Binlog的日志格式
binlog有三種格式:Statement, Row和Mixed.
- 基於SQL語句的復制(statement-based replication, SBR)
- 基於行的復制(row-based replication, RBR)
- 混合模式復制(mixed-based replication, MBR)
Statement
每一條會修改數據的sql都會記錄在binlog中。
優點:不需要記錄每一行的變化,減少了binlog日志量,節約了IO, 提高了性能。
缺點:由於記錄的只是執行語句,為了這些語句能在slave上正確運行,因此還必須記錄每條語句在執行的時候的一些相關信息,以保證所有語句能在slave得到和在master端執行的時候相同的結果。另外mysql的復制,像一些特定函數的功能,slave可與master上要保持一致會有很多相關問題。
相比row能節約多少性能與日志量,這個取決於應用的SQL情況,正常同一條記錄修改或者插入row格式所產生的日志量還小魚statement產生的日志量,但是考慮到如果帶條件的update操作,以及整表刪除,alter表等操作,row格式會產生大量日志,因此在考慮是否使用row格式日志時應該根據應用的實際情況,其所產生的日志量會增加多少,以及帶來的IO性能問題。
Row
5.1.5版本的MySQL才開始支持row level的復制,它不記錄sql語句上下文相關信息,僅保存哪條記錄被修改。
優點: binlog中可以不記錄執行的sql語句的上下文相關的信息,僅需要記錄那一條記錄被修改成什么了。所以row的日志內容會非常清楚的記錄下每一行數據修改的細節。而且不會出現某些特定情況下的存儲過程,或function,以及trigger的調用和觸發無法被正確復制的問題.
缺點:所有的執行的語句當記錄到日志中的時候,都將以每行記錄的修改來記錄,這樣可能會產生大量的日志內容。
新版本的MySQL中對row level模式也被做了優化,並不是所有的修改都會以row level來記錄,像遇到表結構變更的時候就會以statement模式來記錄,如果sql語句確實就是update或者delete等修改數據的語句,那么還是會記錄所有行的變更。
Mixed
從5.1.8版本開始,MySQL提供了Mixed格式,實際上就是Statement與Row的結合。
在Mixed模式下,一般的語句修改使用statment格式保存binlog,如一些函數,statement無法完成主從復制的操作,則采用row格式保存binlog,MySQL會根據執行的每一條具體的sql語句來區分對待記錄的日志形式,也就是在Statement和Row之間選擇一種。
詳述
查看Binlog文件
查看當前服務器使用的二進制文件及大小:
mysql> show binary logs; +------------------+-----------+ | Log_name | File_size | +------------------+-----------+ | mysql-bin.000001 | 126 | | mysql-bin.000002 | 126 | | mysql-bin.000003 | 6819 | | mysql-bin.000004 | 2749 | | mysql-bin.000005 | 1475 | +------------------+-----------+
顯示主服務器使用的二進制文件及大小:
mysql> show master logs; +------------------+-----------+ | Log_name | File_size | +------------------+-----------+ | mysql-bin.000001 | 126 | | mysql-bin.000002 | 126 | | mysql-bin.000003 | 6819 | | mysql-bin.000004 | 2749 | | mysql-bin.000005 | 1475 | +------------------+-----------+
顯示當前使用的二進制文件及所處位置:
mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000005 | 1475 | | | +------------------+----------+--------------+------------------+
binlog_do_db:此參數表示只記錄制定數據庫的二進制日志
binlog_ignore_db:此參數標示不記錄指定的數據庫的二進制日志
需要查看某個具體binlog文件的內容時,以“mysql-bin.000005”為例。
在MySQL客戶端輸入:show binlog events in “mysql-bin.000005”;
效果如下所示:
mysql> show binlog events in 'mysql-bin.000005'; +------------------+-----+-------------+-----------+-------------+---------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +------------------+-----+-------------+-----------+-------------+---------------------------------------+ | mysql-bin.000005 | 4 | Format_desc | 2 | 107 | Server ver: 5.5.51-log, Binlog ver: 4 | | mysql-bin.000005 | 107 | Query | 2 | 181 | BEGIN | | mysql-bin.000005 | 181 | Table_map | 2 | 239 | table_id: 44 (canal_test.company) | | mysql-bin.000005 | 239 | Write_rows | 2 | 287 | table_id: 44 flags: STMT_END_F | | mysql-bin.000005 | 287 | Xid | 2 | 314 | COMMIT /* xid=23915 */ | | mysql-bin.000005 | 314 | Query | 2 | 388 | BEGIN | | mysql-bin.000005 | 388 | Table_map | 2 | 449 | table_id: 35 (canal_test.person) | | mysql-bin.000005 | 449 | Update_rows | 2 | 526 | table_id: 35 flags: STMT_END_F | | mysql-bin.000005 | 526 | Xid | 2 | 553 | COMMIT /* xid=26960 */ | | mysql-bin.000005 | 553 | Query | 2 | 627 | BEGIN | | mysql-bin.000005 | 627 | Table_map | 2 | 688 | table_id: 35 (canal_test.person) | | mysql-bin.000005 | 688 | Write_rows | 2 | 741 | table_id: 35 flags: STMT_END_F | | mysql-bin.000005 | 741 | Xid | 2 | 768 | COMMIT /* xid=26961 */ | | mysql-bin.000005 | 768 | Query | 2 | 842 | BEGIN | | mysql-bin.000005 | 842 | Table_map | 2 | 903 | table_id: 35 (canal_test.person) | | mysql-bin.000005 | 903 | Delete_rows | 2 | 956 | table_id: 35 flags: STMT_END_F | | mysql-bin.000005 | 956 | Xid | 2 | 983 | COMMIT /* xid=26964 */ | +------------------+-----+-------------+-----------+-------------+---------------------------------------+ 17 rows in set (0.00 sec)
或者使用mysqlbinlog命令,在shell終端輸入(假設當前目錄為 ../mysql/data): ../bin/mysqlbinlog mysql-bin.000005
[root@zhuzhonghua1-c6uu8 data]# pwd /usr/local/mysql/data [root@zhuzhonghua1-c6uu8 data]# ../bin/mysqlbinlog mysql-bin.000005 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/; /*!40019 SET @@session.max_insert_delayed_threads=0*/; /*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/; DELIMITER /*!*/; # at 4 #161020 11:07:29 server id 2 end_log_pos 107 Start: binlog v 4, server v 5.5.51-log created 161020 11:07:29 # Warning: this binlog is either in use or was not closed properly. BINLOG ' 8TQIWA8CAAAAZwAAAGsAAAABAAQANS41LjUxLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA== '/*!*/; # at 107 #161020 11:08:50 server id 2 end_log_pos 181 Query thread_id=162 exec_time=1 error_code=0 SET TIMESTAMP=1476932930/*!*/; SET @@session.pseudo_thread_id=162/*!*/; SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/; SET @@session.sql_mode=0/*!*/; 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=8/*!*/; SET @@session.lc_time_names=0/*!*/; SET @@session.collation_database=DEFAULT/*!*/; BEGIN /*!*/; # at 181 # at 239 #161020 11:08:50 server id 2 end_log_pos 239 Table_map: `canal_test`.`company` mapped to number 44 #161020 11:08:50 server id 2 end_log_pos 287 Write_rows: table id 44 flags: STMT_END_F BINLOG ' QjUIWBMCAAAAOgAAAO8AAAAAACwAAAAAAAEACmNhbmFsX3Rlc3QAB2NvbXBhbnkAAwMPDwQsASwB Bg== QjUIWBcCAAAAMAAAAB8BAAAAACwAAAAAAAEAA//4AgAAAAIAZHAIAHNoYW5naGFp '/*!*/; # at 287 #161020 11:08:50 server id 2 end_log_pos 314 Xid = 23915 COMMIT/*!*/; # at 314 #161020 12:03:50 server id 2 end_log_pos 388 Query thread_id=162 exec_time=0 error_code=0 SET TIMESTAMP=1476936230/*!*/; BEGIN /*!*/; # at 388 # at 449 #161020 12:03:50 server id 2 end_log_pos 449 Table_map: `canal_test`.`person` mapped to number 35 #161020 12:03:50 server id 2 end_log_pos 526 Update_rows: table id 35 flags: STMT_END_F BINLOG ' JkIIWBMCAAAAPQAAAMEBAAAAACMAAAAAAAEACmNhbmFsX3Rlc3QABnBlcnNvbgAFAw8D/g8GLAH+ AywBHg== JkIIWBgCAAAATQAAAA4CAAAAACMAAAAAAAEABf//4AEAAAADAHp6aAIAAAABbQQAaGVyZeABAAAA AwB6emgCAAAAAW0HAG5hbmppbmc= '/*!*/; # at 526 #161020 12:03:50 server id 2 end_log_pos 553 Xid = 26960 COMMIT/*!*/; # at 553 #161020 12:05:56 server id 2 end_log_pos 627 Query thread_id=162 exec_time=0 error_code=0 SET TIMESTAMP=1476936356/*!*/; BEGIN /*!*/; # at 627 # at 688 #161020 12:05:56 server id 2 end_log_pos 688 Table_map: `canal_test`.`person` mapped to number 35 #161020 12:05:56 server id 2 end_log_pos 741 Write_rows: table id 35 flags: STMT_END_F BINLOG ' pEIIWBMCAAAAPQAAALACAAAAACMAAAAAAAEACmNhbmFsX3Rlc3QABnBlcnNvbgAFAw8D/g8GLAH+ AywBHg== pEIIWBcCAAAANQAAAOUCAAAAACMAAAAAAAEABf/gBAAAAAQAenpoNBYAAAABbQUAd2hlcmU= '/*!*/; # at 741 #161020 12:05:56 server id 2 end_log_pos 768 Xid = 26961 COMMIT/*!*/; # at 768 #161020 12:06:34 server id 2 end_log_pos 842 Query thread_id=162 exec_time=0 error_code=0 SET TIMESTAMP=1476936394/*!*/; BEGIN /*!*/; # at 842 # at 903 #161020 12:06:34 server id 2 end_log_pos 903 Table_map: `canal_test`.`person` mapped to number 35 #161020 12:06:34 server id 2 end_log_pos 956 Delete_rows: table id 35 flags: STMT_END_F BINLOG ' ykIIWBMCAAAAPQAAAIcDAAAAACMAAAAAAAEACmNhbmFsX3Rlc3QABnBlcnNvbgAFAw8D/g8GLAH+ AywBHg== ykIIWBkCAAAANQAAALwDAAAAACMAAAAAAAEABf/gBAAAAAQAenpoNBYAAAABbQUAd2hlcmU= '/*!*/; # at 956 #161020 12:06:34 server id 2 end_log_pos 983 Xid = 26964 COMMIT/*!*/; DELIMITER ; # End of log file ROLLBACK /* added by mysqlbinlog */; /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
截取上面的一段進行分析:
# at 314 #161020 12:03:50 server id 2 end_log_pos 388 Query thread_id=162 exec_time=0 error_code=0 SET TIMESTAMP=1476936230/*!*/; BEGIN /*!*/;
上面輸出包括如下要素:
- position: 位於文件中的位置,即第一行的(# at 314),說明該事件記錄從文件第314個字節開始
- timestamp: 事件發生的時間戳,即第二行的(#161020 11:07:29)
- exec_time: 事件執行的花費時間
- error_code: 錯誤碼
- server id: 服務器標識(2)
- thread_id: 代理線程id (thread_id=162)
- type:事件類型Query(參考下一章節)
使用mysqlbinlog命令還可以進行數據庫恢復,使用日志進行恢復時,需要依次進行,即最早生成的日志文件要最先恢復:
mysqlbinlog mysql-bin.000001 | mysql -uroot -proot mysqlbinlog mysql-bin.000002 | mysql -uroot -proot .....
Binlog事件
Binlog事件類型
binlog事件類型一共有三個版本:
- v1: Used in MySQL 3.23
- v3: Used in MySQL 4.0.2 though 4.1
- v4: Used in MySQL 5.0 and up
v2出現了很短的時間,並且已經不被支持
現在所使用的MySQL一般都是5.5起了,所以下面陳述的都是v4版的binlog事件類型。
binlog的事件類型一共有以下幾種:
enum Log_event_type { UNKNOWN_EVENT= 0, START_EVENT_V3= 1, QUERY_EVENT= 2, STOP_EVENT= 3, ROTATE_EVENT= 4, INTVAR_EVENT= 5, LOAD_EVENT= 6, SLAVE_EVENT= 7, CREATE_FILE_EVENT= 8, APPEND_BLOCK_EVENT= 9, EXEC_LOAD_EVENT= 10, DELETE_FILE_EVENT= 11, NEW_LOAD_EVENT= 12, RAND_EVENT= 13, USER_VAR_EVENT= 14, FORMAT_DESCRIPTION_EVENT= 15, XID_EVENT= 16, BEGIN_LOAD_QUERY_EVENT= 17, EXECUTE_LOAD_QUERY_EVENT= 18, TABLE_MAP_EVENT = 19, PRE_GA_WRITE_ROWS_EVENT = 20, PRE_GA_UPDATE_ROWS_EVENT = 21, PRE_GA_DELETE_ROWS_EVENT = 22, WRITE_ROWS_EVENT = 23, UPDATE_ROWS_EVENT = 24, DELETE_ROWS_EVENT = 25, INCIDENT_EVENT= 26, HEARTBEAT_LOG_EVENT= 27, IGNORABLE_LOG_EVENT= 28, ROWS_QUERY_LOG_EVENT= 29, WRITE_ROWS_EVENT_V2 = 30, UPDATE_ROWS_EVENT_V2 = 31, DELETE_ROWS_EVENT_V2 = 32, GTID_LOG_EVENT= 33, ANONYMOUS_GTID_LOG_EVENT= 34, PREVIOUS_GTIDS_LOG_EVENT= 35, ENUM_END_EVENT /* end marker */ };
20-22三個類型現在已經被飛起,這三個類型只出現在MySQL的5.1.5-5.1.7版本中。現在(從5.1.7版本開始)使用的WRITE, UPDATE, DELETE三個事件類型分別采用23,24,25。
事件類型簡述
QUERY_EVENT
記錄一條query語句,在基於語句的復制和基於行的復制都會有。
ROTATE_EVENT
二進制日志更換一個新文件,可能因為文件大小達到限制,或者是mysql重啟,亦或者是調用了flush logs命令。
XID_EVENT
Commit事件
WRITE_ROWS_EVENT, UPDATE_ROWS_EVENT, DELETE_ROWS_EVENT
統稱為ROW EVENT, 只有在基於row的復制方式下才會產生。
- WRITE_ROWS_EVENT:包含了要插入的數據
- UPDATE_ROWS_EVENT:包含了修改前的值,也包含了修改后的值
- DELETE_ROWS_EVENT:包含了需要刪除行前的值
TABLE_MAP_EVENT
ROW EVENT之前產生,為的是對ROW EVENT解析提供依據。
FORMAT_DESCRIPTION_EVENT
MySQL根據其定義來解析其他事件
INTVAR_EVET
在statement時使用到,用於自增類型auto_increment.
STOP_EVENT
MySQL停止時,在文件尾加入STOP_EVENT
更多事件類型可以參考MySQL官方文檔:http://dev.mysql.com/doc/internals/en/event-meanings.html
事件類型分析
每個event都有一個19個字節的Binlog Event Header(如下圖), 包括四個字節的timestamep, 一個字節的Binlog Event type, 4個字節的server_id(該id表明binlog的源server是哪個,用來在循環復制中國龍event),四個字節的event包大小,四個字節的下一個event起始偏移,兩個字節的Binlog Event Flag. extra_headers目前的event中沒有涉及到,預留用。
V4 event structure:
一個新的binlog文件都是以FORMAT_DESCRIPTION_EVENT開始的(v4版)。這里就以FORMAT_DESCRIPTION_EVENT為例來解析下(mysql-bin.000005)。通過hexdump -C mysql-bin.000005來查看二進制文件。(下面只列出部分內容)
[root@zhuzhonghua1-c6uu8 data]# hexdump -C mysql-bin.000005 00000000 fe 62 69 6e f1 34 08 58 0f 02 00 00 00 67 00 00 |.bin.4.X.....g..| 00000010 00 6b 00 00 00 01 00 04 00 35 2e 35 2e 35 31 2d |.k.......5.5.51-| 00000020 6c 6f 67 00 00 00 00 00 00 00 00 00 00 00 00 00 |log.............| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 |................| 00000050 38 0d 00 08 00 12 00 04 04 04 04 12 00 00 54 00 |8.............T.| 00000060 04 1a 08 00 00 00 08 08 08 02 00 42 35 08 58 02 |...........B5.X.| 00000070 02 00 00 00 4a 00 00 00 b5 00 00 00 08 00 a2 00 |....J...........| 00000080 00 00 01 00 00 00 0a 00 00 1a 00 00 00 00 00 00 |................| 00000090 01 00 00 00 00 00 00 00 00 06 03 73 74 64 04 21 |...........std.!|
按照官方文檔中的說明來看下FORMAT_DESCRIPTION_EVENT格式:
v4 format description event
binlog是小端字節序的。binlog前4個字節是魔數:0xFE 0x62 0x69 0x6E. 接着是一個FORMAT_DESCRIPTION_EVENT,先看下19個字節的event header. f1 34 08 58即0x580834f1是指時間戳,占4個字節;第5個字節0x0f是type_code即event type(FORMAT_DESCRIPTION_EVENT=15);接着4個字節02 00 00 00 即0x00000002是server_id;再接着4個字節67 00 00 00是event_length=0x00000067=103;然后4個字節6b 00 00 00是下一個next_position=0x0000006b=107;接着兩個字節01 00是flag=0x0001=1,1為LOG_EVENT_BINLOG_IN_USE_F,標識binlog還沒有關閉,binlog關閉后,flag會被設置為0。這樣4+1+4+4+4+2=19個字節。
event data部分分為fixed data和variable data兩部分,其中fixed data是event的固定長度和格式的數據,variable data則是長度變化的數據,比如FORMAT_DESCRIPTION_EVENT的fix data長度是0x54=84字節。下面看下這84=2+50+4+1+27個字節的分配:開始的2個字節0x0004是binlog的版本號;接着的50個字節為mysql-server版本5.5.51-log;接下來4個字節是binlog創建時間,這里是0;然后1個字節是0x13是指之后所有event的公共長度,這里都是19;接着27個字節中每個字節為mysql已知的event(共27個)的fixed data的長度;可以發現FORMAT_DESCRIPTION_EVENT自身的variable data部分為空。