如何避免主從延遲?
讀寫分離對於提升資料庫的併發非常有效,但是,同時也會引來一個問題:主庫和從庫的資料存在延遲,比如你寫完主庫之後,主庫的資料同步到從庫是需要時間的,這個時間差就導致了主庫和從庫的資料不一致性問題。這也就是我們經常說的 主從同步延遲 。
如果我們的業務場景無法容忍主從同步延遲的話,應該如何避免呢(注意:我這裡說的是避免而不是減少延遲)?
這裡提供兩種我知道的方案(能力有限,歡迎補充),你可以根據自己的業務場景參考一下。
強制將讀請求路由到主庫處理
既然你從庫的資料過期了,那我就直接從主庫讀取嘛!這種方案雖然會增加主庫的壓力,但是,實現起來比較簡單,也是我瞭解到的使用最多的一種方式。
比如 Sharding-JDBC 就是採用的這種方案。透過使用 Sharding-JDBC 的 HintManager 分片鍵值管理器,我們可以強制使用主庫。
HintManager hintManager = HintManager.getInstance();
hintManager.setMasterRouteOnly();
// 繼續JDBC操作
對於這種方案,你可以將那些必須獲取最新資料的讀請求都交給主庫處理。
延遲讀取
還有一些朋友肯定會想既然主從同步存在延遲,那我就在延遲之後讀取啊,比如主從同步延遲 0.5s,那我就 1s 之後再讀取資料。這樣多方便啊!方便是方便,但是也很扯淡。
不過,如果你是這樣設計業務流程就會好很多:對於一些對資料比較敏感的場景,你可以在完成寫請求之後,避免立即進行請求操作。比如你支付成功之後,跳轉到一個支付成功的頁面,當你點選返回之後才返回自己的賬戶。
總結
關於如何避免主從延遲,我們這裡介紹了兩種方案。實際上,延遲讀取這種方案沒辦法完全避免主從延遲,只能說可以減少出現延遲的機率而已,實際專案中一般不會使用。
總的來說,要想不出現延遲問題,一般還是要強制將那些必須獲取最新資料的讀請求都交給主庫處理。如果你的專案的大部分業務場景對資料準確性要求不是那麼高的話,這種方案還是可以選擇的。
什麼情況下會出現主從延遲?如何儘量減少延遲?
我們在上面的內容中也提到了主從延遲以及避免主從延遲的方法,這裡我們再來詳細分析一下主從延遲出現的原因以及應該如何儘量減少主從延遲。
要搞懂什麼情況下會出現主從延遲,我們需要先搞懂什麼是主從延遲。
MySQL 主從同步延時是指從庫的資料落後於主庫的資料,這種情況可能由以下兩個原因造成:
從庫 I/O 執行緒接收 binlog 的速度跟不上主庫寫入 binlog 的速度,導致從庫 relay log 的資料滯後於主庫 binlog 的資料;
從庫 SQL 執行緒執行 relay log 的速度跟不上從庫 I/O 執行緒接收 binlog 的速度,導致從庫的資料滯後於從庫 relay log 的資料。
與主從同步有關的時間點主要有 3 個:
主庫執行完一個事務,寫入 binlog,將這個時刻記為 T1;
從庫 I/O 執行緒接收到 binlog 並寫入 relay log 的時刻記為 T2;
從庫 SQL 執行緒讀取 relay log 同步資料本地的時刻記為 T3。
結合我們上面講到的主從複製原理,可以得出:
T2 和 T1 的差值反映了從庫 I/O 執行緒的效能和網路傳輸的效率,這個差值越小說明從庫 I/O 執行緒的效能和網路傳輸效率越高。
T3 和 T2 的差值反映了從庫 SQL 執行緒執行的速度,這個差值越小,說明從庫 SQL 執行緒執行速度越快。
那什麼情況下會出現出從延遲呢?這裡列舉幾種常見的情況:
從庫機器效能比主庫差:從庫接收 binlog 並寫入 relay log 以及執行 SQL 語句的速度會比較慢(也就是 T2-T1 和 T3-T2 的值會較大),進而導致延遲。解決方法是選擇與主庫一樣規格或更高規格的機器作為從庫,或者對從庫進行效能最佳化,比如調整引數、增加快取、使用 SSD 等。
從庫處理的讀請求過多:從庫需要執行主庫的所有寫操作,同時還要響應讀請求,如果讀請求過多,會佔用從庫的 CPU、記憶體、網路等資源,影響從庫的複製效率(也就是 T2-T1 和 T3-T2 的值會較大,和前一種情況類似)。解決方法是引入快取(推薦)、使用一主多從的架構,將讀請求分散到不同的從庫,或者使用其他系統來提供查詢的能力,比如將 binlog 接入到 Hadoop、Elasticsearch 等系統中。
大事務:執行時間比較長,長時間未提交的事務就可以稱為大事務。由於大事務執行時間長,並且從庫上的大事務會比主庫上的大事務花費更多的時間和資源,因此非常容易造成主從延遲。解決辦法是避免大批次修改資料,儘量分批進行。類似的情況還有執行時間較長的慢 SQL ,實際專案遇到慢 SQL 應該進行最佳化。
從庫太多:主庫需要將 binlog 同步到所有的從庫,如果從庫數量太多,會增加同步的時間和開銷(也就是 T2-T1 的值會比較大,但這裡是因為主庫同步壓力大導致的)。解決方案是減少從庫的數量,或者將從庫分為不同的層級,讓上層的從庫再同步給下層的從庫,減少主庫的壓力。
網路延遲:如果主從之間的網路傳輸速度慢,或者出現丟包、抖動等問題,那麼就會影響 binlog 的傳輸效率,導致從庫延遲。解決方法是最佳化網路環境,比如提升頻寬、降低延遲、增加穩定性等。
單執行緒複製:MySQL5.5 及之前,只支援單執行緒複製。為了最佳化複製效能,MySQL 5.6 引入了 多執行緒複製,MySQL 5.7 還進一步完善了多執行緒複製。
複製模式:MySQL 預設的複製是非同步的,必然會存在延遲問題。全同步複製不存在延遲問題,但效能太差了。半同步複製是一種折中方案,相對於非同步複製,半同步複製提高了資料的安全性,減少了主從延遲(還是有一定程度的延遲)。MySQL 5.5 開始,MySQL 以外掛的形式支援 semi-sync 半同步複製。並且,MySQL 5.7 引入了 增強半同步複製 。