最近面試了十幾個同學,關于 MySQL 主從延時問題,筆者一般都會問。
- MySQL 主從延時的原因是什么?
- 具體哪個環節發生延時?
- 如何解決呢?
對于這“三連問”,極少有同學能通關,甚至有同學連主從復制原理都不清楚。
這個并不是純粹的八股文,因為在實際工作場景中,很多同學都遇到過。
不多說,上文章目錄。
一、什么是主從延時?
有時候我們遇到從數據庫中獲取不到信息的詭異問題時,會糾結于代碼中是否有一些邏輯會把之前寫入的內容刪除,但是你又會發現,過了一段時間再去查詢時又可以讀到數據了,這基本上就是主從延遲在作怪。
主從延遲,其實就是“從庫回放” 完成的時間,與 “主庫寫 binlog” 完成時間的差值,會導致從庫查詢的數據,和主庫的不一致。
二、為什么會主從延時?
探討這個問題前,我們需要知道主從復制的原理。
1.主從復制原理
MySQL 的主從復制是依賴于 binlog,也就是記錄 MySQL 上的所有變化并以二進制形式保存在磁盤上二進制日志文件。
主從復制就是將 binlog 中的數據從主庫傳輸到從庫上,一般這個過程是異步的,即主庫上的操作不會等待 binlog 同步地完成。
詳細流程如下:
- 主庫寫 binlog:主庫的更新 SQL(update、insert、delete) 被寫到 binlog;
- 主庫發送 binlog:主庫創建一個 log dump 線程來發送 binlog 給從庫;
- 從庫寫 relay log:從庫在連接到主節點時會創建一個 IO 線程,以請求主庫更新的 binlog,并且把接收到的 binlog 信息寫入一個叫做 relay log 的日志文件;
- 從庫回放:從庫還會創建一個 SQL 線程讀取 relay log 中的內容,并且在從庫中做回放,最終實現主從的一致性。
2.主從延時原因
我們分析一下主從復制的過程。
MySQL 的主從復制都是單線程的操作,主庫對所有 DDL 和 DML 產生 binlog,binlog 是順序寫,所以效率很高。
Slave 的 Slave_IO_Running 線程會到主庫取日志,放入 relay log,效率會比較高。
Slave 的 Slave_SQL_Running 線程將主庫的 DDL 和 DML 操作都在 Slave 實施,DML 和 DDL 的 IO 操作是隨機的,不是順序的,因此成本會很高。
還可能是 Slave 上的其他查詢產生 lock 爭用,由于 Slave_SQL_Running 也是單線程的,所以一個 DDL 卡住了,需要執行 10 分鐘,那么所有之后的 DDL 會等待這個 DDL 執行完才會繼續執行,這就導致了延時。
總結一下主從延遲的主要原因:主從延遲主要是出現在 “relay log 回放” 這一步,當主庫的 TPS 并發較高,產生的 DDL 數量超過從庫一個 SQL 線程所能承受的范圍,那么延時就產生了,當然還有就是可能與從庫的大型 query 語句產生了鎖等待。
三、如何解決主從延時?
1.主從延遲情況
我們先看看,哪些情況會導致主從延時:
- 從庫機器性能:從庫機器比主庫的機器性能差,只需選擇主從庫一樣規格的機器就好。
- 從庫壓力大:可以搞了一主多從的架構,還可以把 binlog 接入到 Hadoop 這類系統,讓它們提供查詢的能力。
- 從庫過多:要避免復制的從節點數量過多,從庫數據一般以3-5個為宜。
- 大事務:如果一個事務執行就要 10 分鐘,那么主庫執行完后,給到從庫執行,最后這個事務可能就會導致從庫延遲 10 分鐘啦。日常開發中,不要一次性 delete 太多 SQL,需要分批進行,另外大表的 DDL 語句,也會導致大事務。
- 網絡延遲:優化網絡,比如帶寬 20M 升級到 100M。
- MySQL 版本低:低版本的 MySQL 只支持單線程復制,如果主庫并發高,來不及傳送到從庫,就會導致延遲,可以換用更高版本的 MySQL,支持多線程復制。
2.主從延時解決方案
面試時,有些同學能回答出使用緩存、查詢主庫、提升機器配置等,僅僅這些么?
最容易想到的方法,縮短主從同步時間:
- 提升從庫機器配置,可以和主庫一樣,甚至更好;
- 避免大事務;
- 搞多個從庫,即一主多從,分擔從庫查詢壓力;
- 優化網絡寬帶;
- 選擇高版本 MySQL,支持主庫 binlog 多線程復制。
也可以從業務場景考慮:
- 使用緩存:我們在同步寫數據庫的同時,也把數據寫到緩存,查詢數據時,會先查詢緩存,不過這種情況會帶來 MySQL 和 redis 數據一致性問題。
- 查詢主庫:直接查詢主庫,這種情況會給主庫太大壓力,核心場景可以使用,比如訂單支付。
如果能把上面基本回答出來,就已經非常厲害了,還有么?
其實還可以在 MySQL 架構上來考慮。
主庫對數據安全性較高,設置配置如下:
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
而 slave 不需要這么高的數據安全,完全可以將 sync_binlog 設置為 0,或者關閉 binlog,innodb_flushlog 也可以設置為 0,來提高 sql 的執行效率。
- 架構方案:使用多臺 slave 來分攤讀請求,再從這些 slave 中取一臺專用的服務器,只作為備份用,不進行其他任何操作,比如設置 sync_binlog 為0,或者關閉 binglog 等,提升從庫查詢性能。
再問一下,還有么?可以在評論區討論~
四、后記
再回過頭來看這個問題,估計很多同學能回答出一二,但是這個不能成為你的加分項。
面對如此激烈的競爭環境,同樣一個問題,你就需要比別人掌握得更多,回答得更全面,面試官才能對你刮目相看。
其實筆者當年面試小米時,也面試過這個問題,當時就是基于上面回答的。
后來的一次 MySQL 分享,講得還不錯,當時主管就說,筆者的 MySQL 掌握得挺好的,記得當時面試時問過一個 MySQL 主從復制問題,他都能回答到非常底層。
沒想到,這都一年多了,當時的那場面試,居然給主管留下那么深刻的印象。
作者丨樓仔
來源丨公眾號:樓仔(ID:gh_8de52dba3fda)