首頁 >系統教程 >Linux >分析mysql主從同步異步的使用場景

分析mysql主從同步異步的使用場景

WBOY
WBOY轉載
2024-01-05 11:53:311082瀏覽

之所以進行這塊內容的研究,主要針對先前遇到的兩個未解的疑惑:

a. 線上有個系統,半同步狀態常從半同步變成異步,然後又馬上恢復為半同步,具體原因未明,雖然之前也猜得八九不離十,但還是未完全確定。

b. 前段時間因為有業務場景需求,做了跨機房非同步複製測試。當mysql寫qps非常高的時候,發現很多日誌還沒來得及發送到從庫,也就是binlog日誌在主庫的產生速度大於傳送到從庫的速度,這個速度差一直存在,因此當主庫持續高壓力地產生binlog的時候,越來越多的binlog沒有傳送到從庫,但當時的網路流量也才18M/S左右(一主一從),從常規的知識認為,千兆的網路傳送的速度可以達到100M,而目前的主從之間的binlog傳送速度只達到了18M左右,原因是什麼?是網路問題?還是其他原因。

主從複製原理 Dump線程與io線程

當主從複製關係建立之後,主庫上有個dump線程,用來傳送在主庫產生的binlog日誌的,而從庫上的io線程,則用來接收由dump線程通過網絡傳送到從庫的binlog日誌,並負責將其寫入relay log 中去。這就是主從複製的機制, 同時,由於是非同步複製,傳送過程不需要ack的確認。

疑問也正在這裡——因為是非同步傳輸,如果單純地理解為binlog檔案直接網路傳送,這個速度應該很快才是,但實際情況:在我們的測試環境中,binlog日誌的傳送速度才18M/s ,小於日誌產生的22M/s左右的速率。為什麼只有這個速度,而沒有把網路頻寬用滿?原因是什麼?

日誌傳送細節

主從複製的結構中,主庫上的dump線程跟從庫上的io線程各自有一個,所以不存在多線程地並發發送跟接收的情況,只需要了解binlog dump線程的工作機制,就能了解所有的細節。

透過解析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 .

函數Binlog_sender::send_events 就是發送binlog中的event事件的函數:

函數入參:

#end_pos,目前讀到的binlog檔案的最末尾位置。

log_cache,記錄是目前傳送的日誌的訊息,包含已經傳送的binlog日誌的位置,以及binlog日誌檔。

函數邏輯分析:

#如果目前已傳送的位置log_pos小於已取得的檔案的末尾位置end_pos.則表示還有binlog日誌未傳送,進入循環。

循環體內:

a. 先呼叫函數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給上層函數。

c. log_pos= my_b_tell(log_cache); 更新log_pos位置,也就是將讀binlog位置的遊標前移到目前位置。

d. 接著呼叫send_packet() 函數進行binlog的傳送。

原來, 不管目前還有多少binlog沒有同步到從庫,主庫發送binlog的粒度依然是一個event的發送,在發送之前,還需要檢查event的型別。因為是小包發送,所以網路的流量並不大。

但需要說明一下出現這個現象的前提條件:我們測試環境中,當時資料庫的寫入qps達到了50000以上,所以需要發送的event特別多,即使是異步,也導致單線程的dump線程來不及發送目前產生的日誌。

當寫的qps巨大的時候,確實存在來不及發送日誌的情景。

總結

現在,再來回頭看線上遇到的問題,“同步狀態經常從半同步狀態變成異步狀態,然後又被及時復原到半同步狀態”,原因是該資料庫是一個分析系統,有時會做批量的更新,以及批量的導入。同時,資料庫設定的binlog格式為row模式,對於一個更新多行的事務,裡麵包含很多的event(一行是一個event), 所以發送這個事務的binlog耗時會比較長,無法在1秒鐘內發送完成(半同步的timeout時間設定為1),所以半同步狀態變成了非同步。

以上是分析mysql主從同步異步的使用場景的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:linuxprobe.com。如有侵權,請聯絡admin@php.cn刪除