MySQL binlog相關原始碼淺析
整理的內容主要針對以下四個問題:
- binlog檔案的邏輯、物理表現形式
- slave io到底是怎麼連上Master的
- sql執行緒是怎麼讀取二進位制資訊,應用到本地MySQL的
- binlog_format=ROW格式下,MySQL是怎麼用二進位制儲存一行資料中不同資料型別的
一、binlog檔案和檔案的邏輯、物理表現形式
1. binlog檔案
binlog檔案主要包括:
- mysql-bin.000001
- mysql-bin.index
其中:
mysql-bin.index內儲存著server中未purge的binlog檔案,以“文字”形式保留的。
2.binlog檔案格式
mysql-bin.index沒啥可說的,我們專門看一下二進位制binlog檔案。
binlog檔案格式有以下特點:
- binlog是由event組成,event是binlog的邏輯最小單元。
- 檔案頭的頭四個位元組為BINLOG_MAGIC(fe 62 69 6e)
- 緊接著這四個位元組的是 descriptor event : FORMAT_DESCRIPTION_EVENT
- 檔案的末尾是 log-rotation event: ROTATE_EVENT
- 這兩個event中間是各種不同的event,每個event代表Master上不同的操作。
下面對基本比較關鍵的概念進行說明:
- BINLOG_MAGIC
利用hexdump -C 讀取mysql-bin.000005的內容,
這裡分別以十六進位制和ASCII碼展示顯示,我們可以看到binlog的頭四個位元組是固定的:fe 62 69 6e,後三個字元ASCII碼為bin,指明展示一個binlog檔案。這四個位元組成為 BINLOG_MAGIC。
- event
mysqlbinlog -vvv 讀取mysql-bin.000011的內容示例:
這裡可以看到,第一個event是FORMAT_DESCRIPTION_EVENT,它記錄了這個binlog的版本(MySQL 5.0以後binlog 的版本都是4)。最後一個event,是ROTATE_EVENT,它記錄了切換到下一個binlog檔案的檔名。
在它們兩個之間是各種其他的event,目前的event型別有:
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 = 30,
UPDATE_ROWS_EVENT = 31,
DELETE_ROWS_EVENT = 32,
GTID_LOG_EVENT= 33,
ANONYMOUS_GTID_LOG_EVENT= 34,
PREVIOUS_GTIDS_LOG_EVENT= 35,
ENUM_END_EVENT
/* end marker */
};
當然,內部已經有部分event被棄用了,我們不一一列舉,這裡只簡單介紹幾個平時經常見到的:
- QUERY_EVENT :用於具體的SQL文字。如果binlog_format=statement方式下,insert,update,delete等各種SQL都是以Query event記錄下來的。
- WRITE_ROWS_EVENT,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT : 在binlog_format=row方式下,insert,update,delete操作的行資訊分別以這三種event記錄下來。
- GTID_LOG_EVENT:5.6的GTID模式下,每個事務的GTID序號被記錄到這種EVENT中。
- PREVIOUS_GTIDS_LOG_EVENT :5.6的GTID模式下,這個event記錄了生成這個binlog之前,MySQL已經執行的所有事務的GTID集合
二、slave io在原始碼中是怎麼連上Master的
這裡主要描述原始碼中的函式呼叫關係
1. slave如何註冊並請求master 的binlog
slave io執行緒對應的入口函式為sql/rpl_slave.cc:handle_slave_io()
該函式核心主要是做了以下三個事情:
- safe_connect(thd, mysql, mi)
- register_slave_on_master(mysql, mi, &suppress_warnings)
- request_dump(thd, mysql, mi, &suppress_warnings)
- event_len= read_event(mysql, mi, &suppress_warnings);
也就是說,它先以標準的連線方式連上master MySQL,然後把自己註冊到master上去,接著呼叫request_dump向master請求binlog資料,最後一個一個event讀取並存放到本地relay log中。
- safe_connect
連線MySQL的標準方式,MySQL c的connector也是用這種方式連線MySQL server的。
- register_slave_on_master
- request_dump
根據GTID來進行判斷,如果是GTID模式,那麼把本地執行的GTID集合及其他相關資訊傳給master;如果不是GTID模式,那麼就把master log file和Pos傳給主庫。主庫如何根據這些資訊來傳送binlog的event,參考下節。
- read_event
read_event呼叫了cli_safe_read(),cli_safe_read()呼叫了my_net_read(),等待主庫將binlog資料發過來
也就是說,read_event被動的從網路中接受主庫發過來的資訊。
2. master 如何處理slave的 binlog 請求
MySQL處理各種命令的核心函式為:sql/sql_parse.cc:dispatch_command
該函式會根據使用者的請求來確定做什麼事情,
- COM_REGISTER_SLAVE則呼叫register_slave(thd, (uchar*)packet, packet_length)註冊slave
- COM_BINLOG_DUMP_GTID 則呼叫com_binlog_dump_gtid(thd, packet, packet_length);
- COM_BINLOG_DUMP 則呼叫com_binlog_dump(thd, packet, packet_length);
我們這裡以com_binlog_dump為例介紹master怎麼傳送binlog event給slave的。
com_binlog_dump核心程式碼為:
- kill_zombie_dump_threads(&slave_uuid);
- mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, NULL)
kill_zombie_dump_threads()函式:如果新的server_id相同的slave註冊上來,master會移除跟該slave的server_id匹配的的binlog dump執行緒
com_binlog_dump()會呼叫mysql_binlog_send()來開啟檔案,將檔案指標挪到指定位置,讀取檔案,將一個個的event按照事件順序發給slave。
綜上
MySQL複製需要slave先註冊到Master,再向Master提交binlog和POS,請求傳送binlog。Master接收到請求後,先做一系列驗證,開啟本地binlog檔案,按照內部event的順序,依序發給slave。
三、sql執行緒在原始碼中是怎麼讀取二進位制資訊,應用到本地MySQL的
sql執行緒在5.6引入了db級別的並行,所以有兩個入口
1
2
|
handle_slave_worker
handle_slave_sql
|
handle_slave_worker執行緒是主要幹活的函式,handle_salve_sql函式作為協調器會啟動和分配worker執行緒。
handle_slave_sql函式主要呼叫了slave_worker_exec_job。
slave_worker_exec_job的主要功能:
- job_item= pop_jobs_item(worker, job_item); ev= static_cast<Log_event*>(job_item->data);
- error= ev->do_apply_event_worker(worker);
該函式做到事情其實就是 從handle_salve_sql()獲得具體的event(ev),然後呼叫ev->do_apply_event_worker(worker),利用c++的多型特性,呼叫真正的event的do_apply_event虛擬函式,以便將不同的event的操作在本地做一遍。
這裡需要大家回顧一下event的概念了,event是binlog的最小單元,所有的event的父類是Log_event(抽象基類),它定義了一系列虛擬函式,其中就包括我們這裡呼叫的函式:
這裡舉一個insert語句對應的的Write_rows_log_event例子,簡單說明一下資料是怎麼應用到本地MySQL的。
Write_rows_log_event,Update_rows_log_event和Delete_rows_log_event的do_apply_event都是呼叫的它的基類Rows_log_event的do_apply_event。
Rows_log_event的do_apply_event主要功能如下:
- m_table= const_cast <relay_log_info* style=”-webkit-print-color-adjust: exact;”> (rli)->m_table_map.get_table(m_table_id);
- error= (this->*do_apply_row_ptr)(rli);
get_table()先從table map中獲得對應的table的資訊,然後呼叫do_apply_row_ptr函式指標指向的函式來將event對應操作應用到本地MySQL。
do_apply_row_ptr函式指標可能指向以下幾種不同的函式:
- do_hash_scan_and_update
- do_index_scan_and_update
- do_table_scan_and_update
- do_apply_row
Write_rows_log_event是insert,不用查詢資料,所以它呼叫的是do_apply_row。
do_apply_row主要的功能就是呼叫了do_exec_row ….汗
do_exec_row是Write_rows_log_event自己實現的,它的主要功能是呼叫了 write_row ….汗
write_row也是Write_rows_log_event自己實現的,它的主要功能是:m_table->file->ha_start_bulk_insert(estimated_rows);也就是說,它直接把這一行資料交給了儲存引擎,讓儲存引擎把資料給插進去。
四、binlog_format=ROW 格式下,MySQL是怎麼用二進位制儲存各種不同的資料型別的
在sql/log_event.cc:log_event_print_value()函式中詳細描述了MySQL的各種資料型別的二進位制表現形式,摘錄如下
這裡有幾個比較有意思的地方:
- MYSQL_TYPE_STRING 型別,它的length佔用的位元組數是不固定的。如果string長度不到大於255,則需要佔用2個位元組,所以,MySQL在定義String型別的欄位時,255是一個坎,如果欄位不可能超過255個位元組,不建議定義長度的時候超過255,否則MySQL儲存資料時會白白浪費一個位元組。
- MYSQL_TYPE_DATETIME型別。它儲存的格式就是把年-月-日 時:分:秒按順序儲存下來的,比如:2015-10-10 22:45:55儲存下來就儲存為20151010224545。這種儲存格式比較浪費,所以位元組數相比timestamp佔用的也比較多。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26250550/viewspace-1979601/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Webpack相關原理淺析Web
- ThreadLocal 原始碼淺析thread原始碼
- String原始碼淺析原始碼
- webmagic原始碼淺析Web原始碼
- Lifecycle原始碼淺析原始碼
- redux 原始碼淺析Redux原始碼
- quicklink原始碼淺析UI原始碼
- Redux原始碼淺析Redux原始碼
- Koa 原始碼淺析原始碼
- 淺析Redux原始碼Redux原始碼
- 【MySQL】五、sync_binlog innodb_flush_log_at_trx_commit 淺析MySqlMIT
- Flutter 原始碼系列:DropdownButton 原始碼淺析Flutter原始碼
- Discuz! Q 原始碼淺析原始碼
- 【QT】QThread原始碼淺析QTthread原始碼
- Paging Library原始碼淺析原始碼
- String 原始碼淺析(一)原始碼
- RXSwift原始碼淺析(二)Swift原始碼
- Guava原始碼淺析——JoinerGuava原始碼
- MySQL中的binlog相關命令和恢復技巧MySql
- MySQL多版本併發控制機制(MVCC)-原始碼淺析MySqlMVC原始碼
- Single-spa 原始碼淺析原始碼
- Android桌面Launcher原始碼淺析Android原始碼
- 【QT】 QThread部分原始碼淺析QTthread原始碼
- react-window 原始碼淺析React原始碼
- Flutter 之 InheritWidget 原始碼淺析Flutter原始碼
- Timer機制原始碼淺析原始碼
- RecyclerView動畫原始碼淺析View動畫原始碼
- react原始碼淺析(三):ReactChildrenReact原始碼
- react原始碼淺析(三):ReactElementValidatorReact原始碼
- react原始碼淺析(三):ReactElementReact原始碼
- Spring-IOC原始碼淺析Spring原始碼
- react-router 原始碼淺析React原始碼
- MySQL核心原始碼解讀-SQL解析之解析器淺析MySql原始碼
- Runtime原始碼淺析(內部分享)原始碼
- String 原始碼淺析————終結篇原始碼
- React-router4原始碼淺析React原始碼
- react原始碼淺析(四):react-isReact原始碼
- Java7 ConcurrentHashMap原始碼淺析JavaHashMap原始碼
- 淺析webpack原始碼之前言(一)Web原始碼