《MySQL主從同步延遲的幾個場景》要點:
本文介紹了MySQL主從同步延遲的幾個場景,希望對您有用。如果有疑問,可以聯(lián)系我們。
之所以進行這塊內(nèi)容的研究,主要針對之前遇到的兩個未解的疑惑:
a.?線上有個系統(tǒng),半同步狀態(tài)經(jīng)常從半同步變成異步,然后又馬上恢復為半同步,具體原因未明,雖然之前也猜得八九不離十,但還是未完全確定.
b.?前段時間因為有業(yè)務場景需求,做了跨機房異步復制測試.當mysql寫qps非常高的時候,發(fā)現(xiàn)很多日志還沒有來得及發(fā)送到從庫,也就是binlog日志在主庫的產(chǎn)生速度大于傳送到從庫的速度,這個速度差一直存在,因此當主庫在持續(xù)高壓力地產(chǎn)生binlog的時候,越來越多的binlog沒有傳送到從庫,但當時的網(wǎng)絡流量也才18M/S左右(一主一從),從常規(guī)的知識認為,千兆的網(wǎng)絡傳送的速度可以達到100M,而當前的主從之間的binlog傳送速度只達到了18M左右,原因是什么? 是網(wǎng)絡問題? 還是其他原因.
當主從復制關系建立之后,主庫上有個dump線程,用來傳送在主庫產(chǎn)生的binlog日志的,而從庫上的io線程,則用來接收由dump線程通過網(wǎng)絡傳送到從庫的binlog日志,并負責將其寫入relay log 中去. 這就是主從復制的機制, 同時,由于是異步復制,傳送過程不需要ack的確認.
疑問也正在此處——因為是異步傳輸,如果單純地理解為binlog文件直接網(wǎng)絡傳送,這個速度應該很快才是,但實際情況:在我們的測試環(huán)境中,binlog日志的傳送速度才18M/s ,小于日志產(chǎn)生的22M/s左右的速度.為什么只有這個速度,而沒有把網(wǎng)絡帶寬用滿?原因是什么?
主從復制的結構中,主庫上的dump線程跟從庫上的io線程各自有一個,所以不存在多線程地并發(fā)發(fā)送跟接收的情況,只需要了解binlog dump線程的工作機制,就能了解所有的細節(jié).
通過解析binlog文件,我們可以知道,一個事務可以包含多個event, 下面是一個最簡單的事物的在binlog中所記錄的信息:
# at 33580
#170531 17:22:53 server id 153443358 ?end_log_pos 33645 CRC32 0x4ea17869 ???????GTID ???last_committed=125 ?????sequence_number=126
SET @@SESSION.GTID_NEXT= ‘e1028e43-4123-11e7-a3c2-005056aa17e6:198’/*!*/;
# at 33645
#170531 17:22:53 server id 153443358 ?end_log_pos 33717 CRC32 0x66820e00 ???????Query ??thread_id=4 ????exec_time=0 ????error_code=0
SET TIMESTAMP=1496222573/*!*/;
BEGIN
/*!*/;
# at 33717
#170531 17:22:53 server id 153443358 ?end_log_pos 33770 CRC32 0x22ddf25e ???????Table_map: `test`.`xcytest` mapped to number 222
# at 33770
#170531 17:22:53 server id 153443358 ?end_log_pos 33817 CRC32 0x61051ea0 ???????Write_rows: table id 222 flags: STMT_END_F
BINLOG ‘
bYsuWRMeXCUJNQAAAOqDAAAAAN4AAAAAAAEABHRlc3QAB3hjeXRlc3QAAgMPAlgCAl7y3SI=
bYsuWR4eXCUJLwAAABmEAAAAAN4AAAAAAAEAAgAC//x9AAAABQBzZGZhc6AeBWE=
‘/*!*/;
### INSERT INTO `test`.`xcytest`
### SET
### ??@1=125 /* INT meta=0 nullable=0 is_null=0 */
### ??@2=’sdfas’ /* VARSTRING(600) meta=600 nullable=1 is_null=0 */
# at 33817
#170531 17:22:53 server id 153443358 ?end_log_pos 33848 CRC32 0x630805b4 ???????Xid = 303
COMMIT/*!*/;
每一個at xxxxx段,是一個event .
函數(shù)Binlog_sender::send_events?就是發(fā)送binlog中的event事件的函數(shù):
我們來解析一下這個函數(shù):
函數(shù)入?yún)?/strong>:end_pos,當前讀到的binlog文件的最末尾位置.
log_cache,記錄是當前傳送的日志的信息,包含已經(jīng)傳送的binlog日志的位置,以及binlog日志文件.
函數(shù)邏輯分析:
如果當前已發(fā)送的位置log_pos小于已獲取到的文件的末尾位置end_pos.則表明還有binlog日志未發(fā)送,進入循環(huán).
循環(huán)體內(nèi):
a.?首先調用函數(shù)read_event,獲取一個事件event.
b.?Log_event_type?event_type= (Log_event_type)event_ptr[EVENT_TYPE_OFFSET];
該語句用來獲取事件event的類型,然后進行類型檢查
check_event_type(event_type, log_file, log_pos),如果沒有通過檢查,直接返回1給上層函數(shù).
c.?log_pos= my_b_tell(log_cache); 更新log_pos位置,也就是將讀binlog位置的游標前移到當前位置.
d.?然后調用send_packet() 函數(shù)進行binlog的發(fā)送.
原來, 不管當前還有多少binlog沒有同步到從庫,主庫發(fā)送binlog的粒度依然是一個一個event的發(fā)送,發(fā)送之前,還需要檢查event的類型.因為是小包發(fā)送,所以網(wǎng)絡的流量并不大.
但需要說明一下出現(xiàn)這個現(xiàn)象的前提條件:我們測試環(huán)境中,當時數(shù)據(jù)庫的寫qps達到了50000以上,所以需要發(fā)送的event特別多,即使是異步,也導致單線程的dump線程來不及發(fā)送當前產(chǎn)生的日志.
當寫的qps巨大的時候,確實存在來不及發(fā)送日志的情景.
現(xiàn)在,再來回頭看線上遇到的問題,“同步狀態(tài)經(jīng)常從半同步狀態(tài)變成異步狀態(tài),然后又被及時復原到半同步狀態(tài)”,原因是該數(shù)據(jù)庫是一個分析系統(tǒng),有時候會做批量的更新,以及批量的導入.同時,數(shù)據(jù)庫設置的binlog格式為row模式,對于一個更新多行的事務,里面包含很多的event(一行是一個event), 所以發(fā)送這個事務的binlog耗時會比較長,無法在1秒鐘內(nèi)發(fā)送完成(半同步的timeout時間設置為1),所以半同步狀態(tài)變成了異步.
文章來自微信公眾號:數(shù)據(jù)庫隨筆
轉載請注明本頁網(wǎng)址:
http://www.snjht.com/jiaocheng/3743.html