使用Spring AOP切面解決資料庫讀寫分離

Sam哥哥發表於2016-07-14

為了減輕資料庫的壓力,一般會使用資料庫主從(master/slave)的方式,但是這種方式會給應用程式帶來一定的麻煩,比如說,應用程式如何做到把資料寫到master庫,而讀取資料的時候,從slave庫讀取。如果應用程式判斷失誤,把資料寫入到slave庫,會給系統造成致命的打擊。

解決讀寫分離的方案很多,常用的有SQL解析、動態設定資料來源。SQL解析主要是通過分析sql語句是insert/select/update/delete中的哪一種,從而對應選擇主從。而動態設定資料來源,則是通過攔截方法名稱的方式來決定主從的,例如:save*(),insert*() 形式的方法使用master庫,select()開頭的,使用slave庫。蠻多公司會使用在方法上標上自定義的@Master、@Slave之類的標籤來選擇主從,也有公司直接就呼叫setxxMaster,setxxSlave之類的程式碼進行主從選擇。

下面我主要介紹一下基於Spring AOP動態設定資料來源這種方式。注意這篇文章是基於自己專案的實際情況的,不是通用的方案,請知曉。

原理圖

1

Spring AOP的切面主要的職責是攔截Mybatis的Mapper介面,通過判斷Mapper介面中的方法名稱來決定主從。

Spring AOP 切面配置

把所有Mybatis介面類都放置在persistence下。配置的切面類是ReadWriteInterceptor。這樣當Mapper介面的方法被呼叫時,會先呼叫這個切面類的readOrWriteDB方法。在這裡需要注意<aop:aspect>中的order=“1” 配置,主要是為了解決切面於切面之間的優先順序問題,因為整個系統中不太可能只有一個切面類。

Spring AOP 切面類實現


  • 覆蓋DynamicDataSource類中的getConnection方法

    ReadWriteInterceptor中的readOrWriteDB方法只是決定選擇主還是從,我們還必須覆蓋資料來源的getConnection方法,以便獲取正確的connection。一般來說,是一主多從,即一個master庫,多個slave庫的,所以還得解決多個slave庫之間負載均衡、故障轉移以及失敗重連線等問題。

    1、負載均衡問題,slave不多,系統併發讀不高的話,直接使用隨機數訪問也是可以的。就是根據slave的臺數,然後產生隨機數,隨機的訪問slave。

    2、故障轉移,如果發現connection獲取不到了,則把它從slave列表中移除,等其回覆後,再加入到slave列表中

    3、失敗重連,第一次連線失敗後,可以多嘗試幾次,如嘗試10次。

    處理業務方法中的@Transactional註解

    我參與的這個專案,大部分業務程式碼是不需要事務的,只有極個別情況需要。那麼按照上面提到的方案,如果不對業務方法中@Transactional註解進行特殊處理的話,主從的選擇會出現問題。大家都知道,如果使用了Spring的事務,那麼在同一個業務方法內,只會呼叫一次資料來源的getConnection方法,如果該業務方法內,呼叫的mapper介面剛好以select開頭的,就會選擇slave庫,那麼接下來呼叫以insert開頭的mapper介面方法時,會把資料寫入到slave庫。如何解決這個問題呢?必須在進入標有@Transactional註解的業務方法前,指定選擇master主庫。可以通過覆蓋DataSourceTransactionManager類中的doBegin方法,如下:

    這樣既可以避免,把資料寫入到從庫的問題。

    總結

    本人的解決方案是基於專案實際的,不一定合適你,我只是展示瞭解決方案而已。當然你可以選擇開源的框架,像阿里的Cobar,360的Atlas。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

使用Spring AOP切面解決資料庫讀寫分離

相關文章