1. 程式人生 > 其它 >MySQL——10、主從延時

MySQL——10、主從延時

1.1 主從延時

1.1.1 如何檢視主從是否延時

可以通過監控 show slave status 命令輸出的Seconds_Behind_Master引數的值,來檢測主從延時:

NULL:表示io_thread或是sql_thread有任何一個發生故障;

0:該值為零,表示主從複製良好;

正值:表示主從已經出現延時,數字越大,表示從庫延遲越嚴重。

1.1.2 為什麼會造成主從延時

由於主庫上可以多客戶端併發的寫入,當主庫的TPS較高時,由於從庫的SQL執行緒是單執行緒的,導致從庫處理速度,可能會跟不上主庫的處理速度,從而造成了延遲。

1.1.3 併發與主從延時的時間

\1) 主庫併發達到1000/s時,從庫的延時會有幾毫秒,幾乎可以忽略。

\2) 主庫併發達到2000/s時,從庫的延時會有幾十毫秒。

\3) 主庫併發達到4000/s,6000/s,8000/s時,此時主庫的壓力很大,都快掛了。從庫的延時會達到幾秒。

1.1.4 主從延時造成的現象

剛剛寫入庫的資料去查,卻沒有查到,可能是發生了主從延時,

比如:剛插入一條訂單資料,然後立即根據id查詢,有很大概率是取不到任何資料的,因為從庫沒來得及更新

注意:

實際上要慮好應該在什麼場景下來用這個mysql主從同步,建議是一般在讀遠遠多於寫,而且讀的時候一般對資料時效性要求沒那麼高的時候,才用mysql主從同步

所以通常來說,對於那種寫了之後,立馬就要保證可以查到的場景,採用強制讀主庫的方式,或資料庫中介軟體,這樣,就可以保證你肯定的可以讀到資料

1.1.5 如何解決主從延時

1、分庫,將主庫拆分成多個;

2、重寫程式碼,插入資料後,不要立即查詢,如果需要立即查詢的,直接讀主庫

​ i. 主庫查詢的Sql查詢語句上加上/master/標識

​ ii. 使用mybatis外掛,new MasterQueryRouter,start和end方法內的全走主庫

MasterQueryRouter內部使用了threadlocal

3、開啟並行複製

Mysql5.7的版本開啟並行複製

通過設定引數slave_parallel_workers>0並且global.slave_parallel_type=‘LOGICAL_CLOCK’開啟並行複製

理解:

一般來說,如果主從延遲較為嚴重:

\1) 架構層面:分庫,將一個主庫拆分為多個。

比如將1個庫拆為4個主庫,每個主庫的寫併發就500/s,此時主從延遲可以忽略不計。

\2) 開啟mysql支援的並行複製,多個庫並行複製。

但如果說某個庫的寫入併發就是特別高,單庫寫併發達到了2000/s,並行複製還是沒意義。28法則,很多時候比如說,就是少數的幾個訂單表,寫入了2000/s,其他幾十個表10/s。

\3) 程式碼層面:重寫程式碼。

寫程式碼的同學,要慎重。當時我們其實短期是讓那個同學重寫了一下程式碼,插入資料之後直接就更新,不要查詢。

\4) 直連主庫。

如果確實存在插入後立馬要求就查詢到,然後根據結果(比如某個狀態)反過來執行一些操作,則可以對這個查詢設定【直連主庫】。

但不推薦這種方法,這麼搞導致讀寫分離的意義就喪失了。

1.1.6 如何強制主庫查詢

1、走主庫查詢的SQL上加/master/標識

2、使用mybatis外掛,new MasterQueryRouter,start和end方法內的全走主庫

理解:

一般的業務系統,對於資料庫的讀操作會遠遠大於寫操作,為了提高查詢效能,降低寫庫的壓力,DBA團隊一般會引入資料庫中介軟體實現讀寫分離,比如atlas。

這種引入中介軟體的方式,對上層應用是透明的,對於應用層來說,就像連線了一個mysql例項。當SQL請求打到資料庫中介軟體上時,查詢的SQL會被自動路由到從庫(多個從庫均衡分配),寫SQL會被路由到主庫。

因為主從同步一定會存在延遲,所以在某些場景下,會出現SQL查詢無法獲取最新資料的情況,比如剛插入一條訂單資料,然後立即根據id查詢,有很大概率是取不到任何資料的,因為從庫沒來得及更新。

解決方法也很直接,強制查詢走主庫,atlas中介軟體就提供了這樣的機制,在SQL前加上/master/,就會被路由到主庫。

方法一:在需要走主庫查詢的SQL上加/master/標識

採用這種方式,我們的mybatis mapper檔案可能存在一些重複的程式碼,比如給某些查詢寫一些強制走主庫的版本,如下:

在業務處理中,也可能要寫多個版本的方法,來區分是否強制查詢走主庫。

(推薦)方法二:通過mybatis外掛機制動態修改SQL

期望業務程式碼可以這樣寫:

public void foo() {

MasterQueryRouter router = new MasterQueryRouter();

router.start(); // start和end方法之間的所有SQL查詢均走主庫

// sqlQuery1

// sqlQuery2

router.end(); // end方法之後的SQL查詢仍然是預設走從庫

// sqlQuery3

// some code

}

sqlQuery1, sqlQuery2查詢走主庫。sqlQuery3查詢預設走從庫。

MasterQueryRouter類通過ThreadLocal保證執行緒安全,且不需要到處傳遞是否強制主庫查詢的標識,在這一點上借鑑了Spring @Transaction的實現。

public class MasterQueryRouter implements Closeable { private static ThreadLocal isForceMaster = ThreadLocal.withInitial(() -> null); /** * 開啟主庫查詢,之後的Sql查詢語句都會走主庫 / public void start() { isForceMaster.set(Boolean.TRUE); } /* * 關閉主庫查詢,之後的Sql查詢都會預設走從庫 / public void end() { isForceMaster.set(Boolean.FALSE); } /* * 是否強制走主庫 * @return / public static boolean forceQueryToMaster() { return Boolean.TRUE.equals(isForceMaster.get()); } /* * 清理資源 * @throws IOException */ @Override public void close() { isForceMaster.remove(); } }

為了及時清理是否走主庫的標識,這兒特意實現了Closeable介面,呼叫方需要保證在當前執行緒執行完畢後,清理資源。

結合lombok,程式碼會更加簡潔,如下:

public void foo() { @Cleanup // try...finally... MasterQueryRouter router = new MasterQueryRouter(); router.start(); // start和end方法之間的所有SQL查詢均走主庫 // some code, contains sql query router.end(); // end方法之後的SQL查詢仍然是預設走從庫 // some code }

@Cleanup表示不需要手動通過try-finally來關閉資源,

這個方案,還可以再進一步,比如可以建立一個註解類@ForceMasterQuery,在需要強制主庫查詢的方法上加上這個註解,然後通過AOP的方式,在方法執行前開啟主庫查詢,方法結束後關閉主庫查詢。