再深刻一點|binlog和relay-log到底長啥樣?

上一篇mysql面試的文章以後收到很多朋友的意見,但願深刻講講複製、日誌的格式這些,今天,咱們就來深挖一下mysql的複製機制到底有哪一些,以及binlog和relay-log的結構究竟是什麼樣子的。mysql

binlog做用

binlog的主要做用是記錄數據庫中表的更改,它只記錄改變數據的sql,不改變數據的sql不會寫入,好比select語句通常不會被記錄,由於他們不會對數據產生任何改動。面試

用一個實際的場景看下binlog產生的過程,準備sql: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';
顯示的結果以下:

binlog

另外,也可使用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表明事件的類型。ui

Server_id是建立事件的服務器ID。spa

End_log_pos表明事件在文件中的結束位置,以上面爲例,第一次查詢的結束位置是723,第二次insert以後文件的開始位置就是從723開始。

Info表明事件信息,是一段可讀的文本內容。

binlog日誌結構

binlog日誌的結構大概是長這樣的,它由索引文件和binlog文件組成,其中binlog事件又包含通用頭、提交頭和事件體3個部分組成。

首先說說索引文件,索引文件的每一行都包含了一個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個部分組成:

  1. 通用頭,包含binlog中全部事件具有的基本信息。
  2. 提交頭,對於不一樣類型的事件來講,提交頭的內容也不盡相同
  3. 事件體,存儲事件的主要數據,一樣對於不一樣類型事件也不一樣。

binlog輪換和清理

從上面的例子咱們也能夠看出來,binlog並不是只有一個,而基於真實的場景來講,始終寫一個binlog文件確定也是不可取的,而binlog輪換主要有3個場景:

  1. 服務器啓動,每次服務器啓動都會生成一個新的binlog文件。
  2. 達到最大大小,能夠經過binlog-cache-size控制大小,達到最大大小後將更換。
  3. 顯示刷新,flush logs將全部日誌寫入磁盤,這時候會建立一個新的文件寫入,從第一個例子也能看出來執行完以後生成了一個新的日誌binlog.000030的文件而且開始的位置是4。

隨着時間的推移,咱們的binlog文件會愈來愈多,這時候有兩種方式能夠清除binlog:

  1. 經過設置expire-logs-days控制想保留的binlog日誌文件天數,系統將會自動清理。
  2. 經過PURGE BINARY LOGS手動清理

relay-log結構

relay-log中繼日誌是鏈接master和slave的核心,咱們來深刻了解一下它的結構和使用。

image-20200909161115718

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都是存在的狀況:

  1. Master收到客戶端請求語句,在語句結束以前向二進制日誌寫入一條記錄,可能包含多個事件。
  2. 此時,一個Slave鏈接到Master,Master的dump線程從binlog讀取日誌併發送到Slave的IO線程。
  3. IO線程從master.info讀取到上一次寫入的最後的位置。
  4. IO線程寫入日誌到relay-log中繼日誌,若是超過指定的relay-log大小,寫入輪換事件,建立一個新的relay-log。
  5. 更新master.info的最後位置
  6. SQL線程從relay-log.info讀取進上一次讀取的位置
  7. SQL線程讀取日誌事件
  8. 在數據庫中執行sql
  9. 更新relay-log.info的最後位置
  10. Slave記錄本身的binlog日誌

可是在這裏IO和SQL線程有會產生重複事件的問題,舉一個場景:

  1. 先記錄中繼日誌,而後更新master.info位置
  2. 此時服務器崩潰,寫入master.info失敗
  3. 服務器恢復,再次同步從master.info獲取到的是上一次的位置,會致使事件重複執行

既然會有這個問題還爲何要這樣作呢?假設反過來,先更新master.info再記錄中繼日誌,這樣帶來的問題就是丟失數據了。而mysql認爲丟失比重複更嚴重,因此要先刷新日誌,保大仍是保小mysql幫你作了決定。

- END -