上一篇mysql面試的文章之后收到不少朋友的意見,希望深入講講復制、日志的格式這些,今天,我們就來深挖一下mysql的復制機制到底有哪一些,以及binlog和relay-log的結構到底是什么樣子的,
binlog作用
binlog的主要作用是記錄資料庫中表的更改,它只記錄改變資料的sql,不改變資料的sql不會寫入,比如select陳述句一般不會被記錄,因為他們不會對資料產生任何改動,
用一個實際的場景看下binlog產生的程序,準備sql:
create table test(text varchar(20));
insert into test values ('test_text');
select * from test;
flush logs;
查看binlog
show binlog events in 'binlog.000029';
顯示的結果如下:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6eSlYWTO-1599757545037)(https://tva1.sinaimg.cn/large/007S8ZIlgy1gikthd6sbbj31720cq7al.jpg)]
另外,也可以使用mysqlbinlog工具來查看binlog的內容:
show variables like 'log_%'; #查看日志目錄
mysqlbinlog --short-form --force-if-open --base64-output=never /usr/local/var/mysql/binlog.000029


從日志我們可以看到執行了創建表的陳述句以及一個Format_desc頭和Ratate輪換事件,這個我們會在后面講到,先看幾個欄位代表的含義,
Log_name代表日志檔案的名稱,比如我這里的查詢是直接查詢binlog.000029,默認的寫法是show binlog events,但是這樣只會查詢到第一個binlog,并不是當前激活狀態的binlog,如果你不知道binlog有哪些,可以用命令:
show binary logs; #查看binlog串列
show master status; #查看最新的binlog

Pos代表檔案開始的位置,
Event_type代表事件的型別,
Server_id是創建事件的服務器ID,
End_log_pos代表事件在檔案中的結束位置,以上面為例,第一次查詢的結束位置是723,第二次insert之后檔案的開始位置就是從723開始,
Info代表事件資訊,是一段可讀的文本內容,
binlog日志結構
binlog日志的結構大概是長這樣的,它由索引檔案和binlog檔案組成,其中binlog事件又包含通用頭、提交頭和事件體3個部分組成,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OMDb3FYJ-1599757545043)(https://tva1.sinaimg.cn/large/007S8ZIlgy1gilcgk2jjij30qe0b4406.jpg)]
首先說說索引檔案,索引檔案的每一行都包含了一個binlog檔案的完整檔案名(類似host-bin.001),一些命令比如flush logs將所有日志寫入磁盤會影響到索引檔案,
每個binlog檔案以若干個binlog事件組成,以格式描述事件(Format_description)作為檔案頭(上面的binlog圖片Format_desc事件),以日志輪換事件(rotate)作為檔案尾,
Format_description包含binlog檔案的服務器資訊、檔案狀態的關鍵資訊等,如果服務器關倍訓者重啟,則會創建一個新的binlog檔案,同時寫入一個新的format_description,他的格式大致如下,
2 binlog-version
string[50] mysql-server version
4 create timestamp
1 event header length
string[p] event type header lengths
日志輪換事件則包含下一個binlog的檔案名以及開始讀取的位置,它由服務器寫完binlog后添加到檔案尾,輪換事件并不會每次都存在,格式如下,
if binlog-version > 1 {
8 position
}
string[p] name of the next binlog
binlog事件包含若干個事務組成的組(group),每個組對應一個事務,如果是create alter陳述句不屬于事務陳述句的話,則他們本身就是一個組,每個組要么全部執行,要么都不執行,

binlog事件結構
每個binlog事件由3個部分組成:
- 通用頭,包含binlog中所有事件具備的基本資訊,
- 提交頭,對于不同型別的事件來說,提交頭的內容也不盡相同
- 事件體,存盤事件的主要資料,同樣對于不同型別事件也不同,
binlog輪換和清理
從上面的例子我們也可以看出來,binlog并非只有一個,而基于真實的場景來說,始終寫一個binlog檔案肯定也是不可取的,而binlog輪換主要有3個場景:
- 服務器啟動,每次服務器啟動都會生成一個新的binlog檔案,
- 達到最大大小,可以通過binlog-cache-size控制大小,達到最大大小后將更換,
- 顯示重繪,flush logs將所有日志寫入磁盤,這時候會創建一個新的檔案寫入,從第一個例子也能看出來執行完之后生成了一個新的日志binlog.000030的檔案并且開始的位置是4,

隨著時間的推移,我們的binlog檔案會越來越多,這時候有兩種方式可以清除binlog:
- 通過設定expire-logs-days控制想保留的binlog日志檔案天數,系統將會自動清理,
- 通過PURGE BINARY LOGS手動清理
relay-log結構
relay-log中繼日志是連接master和slave的核心,我們來深入了解一下它的結構和使用,

relay-log的結構和binlog非常相似,只不過他多了一個master.info和relay-log.info的檔案,
master.info記錄了上一次讀取到master同步過來的binlog的位置,以及連接master和啟動復制必須的所有資訊,
relay-log.info記錄了檔案復制的進度,下一個事件從什么位置開始,由sql執行緒負責更新,
上一篇文章我們提到了整個復制流程的程序大概是這個樣子:

知道binlog和relay-log的結構之后,我們重新梳理一下整個鏈路的流程,這里我們假定master.info和relay-log.info都是存在的情況:
- Master收到客戶端請求陳述句,在陳述句結束之前向二進制日志寫入一條記錄,可能包含多個事件,
- 此時,一個Slave連接到Master,Master的dump執行緒從binlog讀取日志并發送到Slave的IO執行緒,
- IO執行緒從master.info讀取到上一次寫入的最后的位置,
- IO執行緒寫入日志到relay-log中繼日志,如果超過指定的relay-log大小,寫入輪換事件,創建一個新的relay-log,
- 更新master.info的最后位置
- SQL執行緒從relay-log.info讀取進上一次讀取的位置
- SQL執行緒讀取日志事件
- 在資料庫中執行sql
- 更新relay-log.info的最后位置
- Slave記錄自己的binlog日志

但是在這里IO和SQL執行緒有會產生重復事件的問題,舉一個場景:
- 先記錄中繼日志,然后更新master.info位置
- 此時服務器崩潰,寫入master.info失敗
- 服務器恢復,再次同步從master.info獲取到的是上一次的位置,會導致事件重復執行
既然會有這個問題還為什么要這樣做呢?假設反過來,先更新master.info再記錄中繼日志,這樣帶來的問題就是丟失資料了,而mysql認為丟失比重復更嚴重,所以要先重繪日志,保大還是保小mysql幫你做了決定,
關注公眾號:科技繆繆 獲取更多技術知識
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/16502.html
標籤:AI
