通過對《【Mysql源碼分析】基於行的複製實現之「主從關係創建」》瞭解了主從複製的一些原理,本章內容會深刻對binlog、relaylog作講解。並對流程作深刻了解。html
在開始交接以前,咱們帶着幾個問題切入:mysql
在mysql中能夠經過以下命令能夠查看都有哪些binlog日誌,如圖1-1:sql
mysql> show binary logs;
<center>圖1-1 查看binlog日誌</center>安全
在查看binlog時、主有本身的binlog、從也有本身的binlog。bash
若是要查看最新的binlog,能夠經過以下命令查看,如圖1-2服務器
mysql> show master status;
<center>圖1-2 查看最新binlog</center>app
經過以下命令能夠查看當前binlog事件,如圖1-3:ide
mysql> show binlog events;
<center>圖1-3 查看binlog事件</center>函數
除了查看binlog事件之外,咱們還能夠查看relaylog的事件如圖1-4所示,查看事件命令以下:源碼分析
mysql> show relaylog events;
<center>圖1-4 查看relaylog事件</center>
relaylog事件的參數含義和binlog的一致,可參考binlog。
除了查看relaylog事件外,還能夠查看relaylog參數,如圖1-5所示。經過以下命令能夠查看:
mysql> show variables like '%relay%';
<center>圖1-5 relaylog參數 </center>
slave_relay_log_info和slave_master_info分別爲relaylog-info和master-info信息。relaylog-info和master-info信息可使用FILE或這TABLE存儲。relaylog-info有三種形式,分爲爲FILE、TABLE、DUMMY。
<center>圖2-1 查看relaylog-info和master-info </center>
經過relay_log_info_repository和master_info_repository能夠得知relaylog-info和master-info是用什麼形式存儲,如圖2-1所示。
想了解binlog的格式,能夠經過十六進制的形式去查看,如圖3-1所示。
<center>圖3-1 binlog十六進制格式 </center>
查看十六進制格式可使用hexdump命令,命令格式以下:
#hexdump -C mysql-bin.000024
經過hexdump獲得以下內容:
00000000 fe 62 69 6e 41 93 92 5f 0f 0a 00 00 00 79 00 00 |.binA.._.....y..| 00000010 00 7d 00 00 00 01 00 04 00 38 2e 30 2e 32 30 2d |.}.......8.0.20-| 00000020 64 65 62 75 67 00 00 00 00 00 00 00 00 00 00 00 |debug...........| 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 41 93 92 5f 13 |...........A.._.| 00000050 00 0d 00 08 00 00 00 00 04 00 04 00 00 00 61 00 |..............a.| 00000060 04 1a 08 00 00 00 08 08 08 02 00 00 00 0a 0a 0a |................| 00000070 2a 2a 00 12 34 00 0a 28 01 3a 50 74 ce 41 93 92 |**..4..(.:Pt.A..| 00000080 5f 23 0a 00 00 00 1f 00 00 00 9c 00 00 00 80 00 |_#..............| 00000090 00 00 00 00 00 00 00 00 56 4b 92 11 |........VK..|
也能夠mysqlbinlog命令獲得binlog信息,如圖3-2。命令格式以下:
#mysqlbinlog --base64-output='decode-rows' mysql-bin.000024
<center>圖3-2 binlog信息 </center>
能夠經過一張圖解析下圖3-2中內容,從十六進制格式中解析一下binlog格式,如圖3-3所示。
<center>圖3-3 binlog格式解析 </center>
能夠關注下以下內容和圖3-3還有圖3-2中關係:
fe 62 69 6e 對應魔術頭 0xFE 'bin' 41 93 92 5f timestamp時間戳1603441473,對應時間"2020/10/23 16:24:33" 0f event_type對應十進制16對應XID_EVENT 0a 00 00 00 server_id 0x0a對應十進制10 79 00 00 00 event_size 對應十進制121 7d 00 00 00 log_pos 對應十進制125 01 00 flags 狀態 04 00 binlog version對應版本4 38 2e 30 2e 32 30 2d 64 65 62 75 67 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 mysql-server version 對應版本號8.0.20-debug 41 93 92 5f create-timestamp 建立時間戳,對應1603441473,對應時間2020/10/23 16:24:33 13 Event-header-length對應19 00 0d 00 08 00 00 00 00 04 00 04 00 00 00 61 00 04 1a 08 00 00 00 08 08 08 02 00 00 00 0a 0a 0a 2a 2a 00 12 34 00 0a 28 01 Event-type-header-length 對應41個event類型 3a 50 74 ce CRC32 對應 0xce74503a 41 93 92 5f timestamp對應時間2020/10/23 16:24:33 23 event_type對應35對應PREVIOUS_GTIDS_LOG_EVENT 0a 00 00 00 server_id對應服務ID爲10 1f 00 00 00 event_size對應大小31 9c 00 00 00 log_pos對應156 80 00 flags 狀態 00 00 00 00 00 00 00 00 Next pos 56 4b 92 11 CRC 0x11924b56
Event-type-header-length對41爲何是41?能夠看一下源碼,對應libbinlogevents/include/binlog_event.h
enum Log_event_type { /** Every time you add a type, you have to - Assign it a number explicitly. Otherwise it will cause trouble if a event type before is deprecated and removed directly from the enum. - Fix Format_description_event::Format_description_event(). */ UNKNOWN_EVENT = 0, /* 自MySQL8.0.2以來已棄用。它只是一個佔位符,不該該在其餘地方使用。 */ START_EVENT_V3 = 1, //起始事件是二進制日誌版本1至3的二進制日誌的第一個事件。 QUERY_EVENT = 2, //查詢事件用於向binlog發送文本查詢。 STOP_EVENT = 3, //中止事件 ROTATE_EVENT = 4, //將Rotate事件做爲最後一個事件添加到binlog中,以告訴讀者接下來要請求的binlog。 INTVAR_EVENT = 5, //基於整數的會話變量 SLAVE_EVENT = 7, //從事件 APPEND_BLOCK_EVENT = 9, //將塊數據追加到文件 DELETE_FILE_EVENT = 11, //刪除文件事件 RAND_EVENT = 13, //RAND() 函數的 內部狀態。 USER_VAR_EVENT = 14, //用戶變量 FORMAT_DESCRIPTION_EVENT = 15, //格式描述事件是binlog版本4的binlog的第一個事件。它描述了其餘事件的佈局方式。 XID_EVENT = 16, //2PC的事務處理ID,在須要時寫入 COMMIT。 BEGIN_LOAD_QUERY_EVENT = 17, //截斷文件並設置塊數據 EXECUTE_LOAD_QUERY_EVENT = 18, TABLE_MAP_EVENT = 19, //基於行的複製中 使用的第一個事件 聲明如何定義將要更改的表。 /** V1事件號從5.1.16一直使用到mysql-5.6。 */ WRITE_ROWS_EVENT_V1 = 23, UPDATE_ROWS_EVENT_V1 = 24, DELETE_ROWS_EVENT_V1 = 25, /** 主發生了不尋常的事 */ INCIDENT_EVENT = 26, /** 主機在空閒時間發送的心跳事件確保主機聯機狀態爲從機 */ HEARTBEAT_LOG_EVENT = 27, /** 在某些狀況下,有必要把不可忽視的東西送過去數據到從機: 在這種狀況下,從機能夠處理的數據是用於處理它的代碼, 但若是不是,則能夠忽略它辨識。 */ IGNORABLE_LOG_EVENT = 28, ROWS_QUERY_LOG_EVENT = 29, //ROWS_EVENT的查詢 /** 行事件的版本2 */ WRITE_ROWS_EVENT = 30, UPDATE_ROWS_EVENT = 31, DELETE_ROWS_EVENT = 32, GTID_LOG_EVENT = 33, //gtid ANONYMOUS_GTID_LOG_EVENT = 34, //匿名gtid PREVIOUS_GTIDS_LOG_EVENT = 35, //上一個gtid TRANSACTION_CONTEXT_EVENT = 36, //事務上下文事件 VIEW_CHANGE_EVENT = 37, /* 準備了相似於Xid的XA事務終端事件 */ XA_PREPARE_LOG_EVENT = 38, /** UPDATE_ROWS_事件的擴展,容許根據到 binlog_row_value_options配置中。 */ PARTIAL_UPDATE_ROWS_EVENT = 39, TRANSACTION_PAYLOAD_EVENT = 40, ENUM_END_EVENT /* 終點標記 */ };
在mysql5中只有39個event-type。
基於行復制實現的的事件爲TABLE_MAP_EVENT、ROWS_EVENT、ROWS_QUERY_EVENT三大類。
能夠看官方: https://dev.mysql.com/doc/internals/en/row-based-replication.html
<center>圖4-1 主從同步 </center>
在主從同步過程當中、咱們能夠把同步過程分爲10個階段,如圖4-1。
通過對以前的《【Mysql源碼分析】基於行的複製實現之「主從關係創建」》的學習,得知dump線程會接收到COM_BINLOG_DUMP指令後觸發。然而從服務器請求二進制日誌流中分爲幾個參數:
1字節 [12] COM_BINLOG_DUMP 4字節 binlog-pos binlog對應到位置 2字節 flags 狀態 4字節 server-id 服務ID string[EOF] binlog-filename 文件名稱
當接收到COM_BINLOG_DUMP指令後會觸發調用com_binlog_dump方法,com_binlog_dump方法中會調用mysql_binlog_send函數用於發送binlog,如圖圖4-2所示。
<center>圖4-2 發送binlog </center>
void mysql_binlog_send(THD *thd, char *log_ident, my_off_t pos, Gtid_set *slave_gtid_executed, uint32 flags) { //log_ident發送binlog名稱 //pos 當前行號,後續會做爲開始行號 Binlog_sender sender(thd, log_ident, pos, slave_gtid_executed, flags); sender.run(); }
在調用mysql_binlog_send方法後,會調用Binlog_sender::send_events方法發送事件,在發送事件時會調用Binlog_sender::check_event_type方法對事件進行檢測,如圖4-3。
<center>圖4-3 事件檢測方法 </center>
用Binlog_sender::check_event_type檢測方法原型以下:
bool Binlog_sender::check_event_type(Log_event_type type, const char *log_file, my_off_t log_pos) { if (type == binary_log::ANONYMOUS_GTID_LOG_EVENT) { /* 一般狀況下,當自動位置被啓用,由於主設備和從設備 若是主機未使用GTID_MODE=ON,則拒絕鏈接。 可是,若是主機在鏈接後更改了GTID_模式初始化, 或者若是從屬請求複製出如今最後一個匿名事件以前 的事務,則這可能發生。而後生成此錯誤以阻止發送 到從屬服務器的匿名事務。 */ if (m_using_gtid_protocol) { //判斷是否使用gtid DBUG_EXECUTE_IF("skip_sender_anon_autoposition_error", { return false; };); char buf[MYSQL_ERRMSG_SIZE]; snprintf(buf, MYSQL_ERRMSG_SIZE, ER_THD(m_thd, ER_CANT_REPLICATE_ANONYMOUS_WITH_AUTO_POSITION), log_file, log_pos); set_fatal_error(buf); return true; } /* 一般狀況下,當master有GTID_MODE=ON,由於當GTID_MODE=ON。可是,若是主控形狀發生更改,則可能會發生這種狀況當從屬服務器還沒有複製全部匿名交互。 */ else if (get_gtid_mode_from_copy(GTID_MODE_LOCK_NONE) == GTID_MODE_ON) { char buf[MYSQL_ERRMSG_SIZE]; snprintf(buf, MYSQL_ERRMSG_SIZE, ER_THD(m_thd, ER_CANT_REPLICATE_ANONYMOUS_WITH_GTID_MODE_ON), log_file, log_pos); set_fatal_error(buf); return true; } } else if (type == binary_log::GTID_LOG_EVENT) { /* 一般狀況下,當主服務器有GTID_MODE=OFF,由於當 GTID_MODE=OFF。可是,若是主控形狀發生更改,則 可能會發生這種狀況當從機還沒有複製全部GTID時,GTID_MODE關閉交互。 */ if (get_gtid_mode_from_copy(GTID_MODE_LOCK_NONE) == GTID_MODE_OFF) { char buf[MYSQL_ERRMSG_SIZE]; snprintf(buf, MYSQL_ERRMSG_SIZE, ER_THD(m_thd, ER_CANT_REPLICATE_GTID_WITH_GTID_MODE_OFF), log_file, log_pos); set_fatal_error(buf); return true; } } return false; }
<center>圖4-4 Log_event::write </center>
在寫入binlog時會調用Log_event::write進行寫入,如圖4-4。Log_event::write在log_event.h頭文件中。
寫入master-info信息調度以下:
(lldb) bt * thread #36, stop reason = breakpoint 8.1 * frame #0: 0x0000000109a10c80 mysqld`Master_info::write_info(this=0x00007fe416209200, to=0x00007fe418204230) at rpl_mi.cc:666 frame #1: 0x0000000109a0e9ba mysqld`Master_info::flush_info(this=0x00007fe416209200, force=false) at rpl_mi.cc:380 frame #2: 0x0000000109a987eb mysqld`flush_master_info(mi=0x00007fe416209200, force=false, need_lock=false, do_flush_relay_log=false) at rpl_slave.cc:1424 frame #3: 0x0000000109ab4565 mysqld`queue_event(mi=0x00007fe416209200, buf="\a�\x8e_\"\n", event_len=77, do_flush_mi=true) at rpl_slave.cc:7956 frame #4: 0x0000000109a9e063 mysqld`::handle_slave_io(arg=0x00007fe416209200) at rpl_slave.cc:5462 frame #5: 0x000000010ac5a6c5 mysqld`pfs_spawn_thread(arg=0x00007fe4175b28b0) at pfs.cc:2854 frame #6: 0x00007fff7e063305 libsystem_pthread.dylib`_pthread_body + 126 frame #7: 0x00007fff7e06626f libsystem_pthread.dylib`_pthread_start + 70 frame #8: 0x00007fff7e062415 libsystem_pthread.dylib`thread_start + 13
寫入Relay-log-info信息調度以下:
(lldb) bt * thread #41, stop reason = breakpoint 2.1 * frame #0: 0x000000010c36f990 mysqld`Relay_log_info::write_info(this=0x00007f8a53de3400, to=0x00007f8a5512af50) at rpl_rli.cc:2200 frame #1: 0x000000010c3677e1 mysqld`Relay_log_info::flush_info(this=0x00007f8a53de3400, force=true) at rpl_rli.cc:1905 frame #2: 0x000000010c371fe8 mysqld`Relay_log_info::commit_positions(this=0x00007f8a53de3400) at rpl_rli.cc:2730 frame #3: 0x000000010a4b72eb mysqld`Relay_log_info::pre_commit(this=0x00007f8a53de3400) at rpl_rli.h:1986 frame #4: 0x000000010a4b6655 mysqld`ha_commit_trans(thd=0x00007f8a5605fa00, all=true, ignore_global_read_lock=false) at handler.cc:1656 frame #5: 0x000000010aefeab4 mysqld`trans_commit(thd=0x00007f8a5605fa00, ignore_global_read_lock=false) at transaction.cc:241 frame #6: 0x000000010ab202d3 mysqld`mysql_create_db(thd=0x00007f8a5605fa00, db="t136", create_info=0x000070000d37a898) at sql_db.cc:405 frame #7: 0x000000010ac6f026 mysqld`mysql_execute_command(thd=0x00007f8a5605fa00, first_level=true) at sql_parse.cc:3660 frame #8: 0x000000010ac676ad mysqld`mysql_parse(thd=0x00007f8a5605fa00, parser_state=0x000070000d37e028) at sql_parse.cc:5306 frame #9: 0x000000010c24dc6c mysqld`Query_log_event::do_apply_event(this=0x00007f8a520d0620, rli=0x00007f8a53de3400, query_arg="create database t136", q_len_arg=20) at log_event.cc:4804 frame #10: 0x000000010c24af13 mysqld`Query_log_event::do_apply_event(this=0x00007f8a520d0620, rli=0x00007f8a53de3400) at log_event.cc:4415 frame #11: 0x000000010c2458fe mysqld`Log_event::apply_event(this=0x00007f8a520d0620, rli=0x00007f8a53de3400) at log_event.cc:3241 frame #12: 0x000000010c4047e7 mysqld`apply_event_and_update_pos(ptr_ev=0x000070000d37f860, thd=0x00007f8a5605fa00, rli=0x00007f8a53de3400) at rpl_slave.cc:4350 frame #13: 0x000000010c3f2032 mysqld`exec_relay_log_event(thd=0x00007f8a5605fa00, rli=0x00007f8a53de3400, applier_reader=0x000070000d3803d8, in=0x00007f8a520d0620) at rpl_slave.cc:4867 frame #14: 0x000000010c3db543 mysqld`::handle_slave_sql(arg=0x00007f8a52b43200) at rpl_slave.cc:7022 frame #15: 0x000000010d5936c5 mysqld`pfs_spawn_thread(arg=0x00007f8a554bcbf0) at pfs.cc:2854 frame #16: 0x00007fff7e063305 libsystem_pthread.dylib`_pthread_body + 126 frame #17: 0x00007fff7e06626f libsystem_pthread.dylib`_pthread_start + 70 frame #18: 0x00007fff7e062415 libsystem_pthread.dylib`thread_start + 13
Master斷點
#發送binlog (lldb)b mysql_binlog_send #發送binlog包 (lldb)b Binlog_sender::send_packet #循環事件 (lldb)b Binlog_sender::run #發送binlog (lldb)b Binlog_sender::send_binlog #發送心跳包事件 (lldb)b Binlog_sender::send_heartbeat_event
Slave 斷點
#讀取頭文件事件 b Binlog_event_data_istream::read_event_header #填充時間數據 b Binlog_event_data_istream::fill_event_data #讀取下一個事件 b Rpl_applier_reader::read_next_event #slave sal處理 b handle_slave_sql #寫入relay-log-info b Relay_log_info::write_info #relay_log事件 b exec_relay_log_event #寫入binlog或者relaylog斷點 b handler::ha_write_row b MYSQL_BIN_LOG::write_event b binlog_cache_data::write_event #寫入relaylog以後操做 MYSQL_BIN_LOG::after_write_to_relay_log #寫入binlog b MYSQL_BIN_LOG::write_buffer b MYSQL_BIN_LOG::Binlog_ofile::write
複製協議
https://dev.mysql.com/doc/internals/en/replication-protocol.html
事件含義
https://dev.mysql.com/doc/internals/en/event-meanings.html
基於行的二進制日誌記錄
[https://dev.mysql.com/doc/int...
](https://dev.mysql.com/doc/int...