使用Spring AOP切面解決資料庫讀寫分離
為了減輕資料庫的壓力,一般會使用資料庫主從(master/slave)的方式,但是這種方式會給應用程式帶來一定的麻煩,比如說,應用程式如何做到把資料寫到master庫,而讀取資料的時候,從slave庫讀取。如果應用程式判斷失誤,把資料寫入到slave庫,會給系統造成致命的打擊。
解決讀寫分離的方案很多,常用的有SQL解析、動態設定資料來源。SQL解析主要是通過分析sql語句是insert/select/update/delete中的哪一種,從而對應選擇主從。而動態設定資料來源,則是通過攔截方法名稱的方式來決定主從的,例如:save*(),insert*() 形式的方法使用master庫,select()開頭的,使用slave庫。蠻多公司會使用在方法上標上自定義的@Master、@Slave之類的標籤來選擇主從,也有公司直接就呼叫setxxMaster,setxxSlave之類的程式碼進行主從選擇。
下面我主要介紹一下基於Spring AOP動態設定資料來源這種方式。注意這篇文章是基於自己專案的實際情況的,不是通用的方案,請知曉。
原理圖
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方法,如下:
|
這樣既可以避免,把資料寫入到從庫的問題。