佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > Spring AOP切面解決數(shù)據(jù)庫讀寫分離實(shí)例詳解

Spring AOP切面解決數(shù)據(jù)庫讀寫分離實(shí)例詳解

熱門標(biāo)簽:Mysql連接數(shù)設(shè)置 Linux服務(wù)器 阿里云 科大訊飛語音識別系統(tǒng) 電子圍欄 服務(wù)器配置 團(tuán)購網(wǎng)站 銀行業(yè)務(wù)

Spring AOP切面解決數(shù)據(jù)庫讀寫分離實(shí)例詳解

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

解決讀寫分離的方案很多,常用的有SQL解析、動態(tài)設(shè)置數(shù)據(jù)源。SQL解析主要是通過分析sql語句是insert/select/update/delete中的哪一種,從而對應(yīng)選擇主從。而動態(tài)設(shè)置數(shù)據(jù)源,則是通過攔截方法名稱的方式來決定主從的,例如:save*(),insert*() 形式的方法使用master庫,select()開頭的,使用slave庫。蠻多公司會使用在方法上標(biāo)上自定義的@Master、@Slave之類的標(biāo)簽來選擇主從,也有公司直接就調(diào)用setxxMaster,setxxSlave之類的代碼進(jìn)行主從選擇。

下面我主要介紹一下基于Spring AOP動態(tài)設(shè)置數(shù)據(jù)源這種方式。注意這篇文章是基于自己項(xiàng)目的實(shí)際情況的,不是通用的方案,請知曉。

原理圖


 

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

 Spring AOP 切面配置

aop:config expose-proxy="true"> 
 
aop:pointcut id="txPointcut" expression="execution(* com.test..persistence..*.*(..))" /> 
 
aop:aspect ref="readWriteInterceptor" order="1"> 
 
aop:around pointcut-ref="txPointcut" method="readOrWriteDB"/> 
 
/aop:aspect> 
 
/aop:config> 
 
  
 
bean id="readWriteInterceptor" class="com.test.ReadWriteInterceptor"> 
 
  property name="readMethodList"> 
 
   list> 
 
    value>query*/value> 
 
    value>use*/value> 
 
    value>get*/value> 
 
    value>count*/value> 
 
    value>find*/value> 
 
    value>list*/value> 
 
    value>search*/value> 
 
  /list> 
 
 /property> 
 
property name="writeMethodList"> 
 
  list> 
 
    value>save*/value> 
 
    value>add*/value> 
 
    value>create*/value> 
 
    value>insert*/value> 
 
    value>update*/value> 
 
    value>merge*/value> 
 
    value>del*/value> 
 
    value>remove*/value> 
 
    value>put*/value> 
 
    value>write*/value> 
 
  /list> 
 
/property> 
 
/bean> 

把所有Mybatis接口類都放置在persistence下。配置的切面類是ReadWriteInterceptor。這樣當(dāng)Mapper接口的方法被調(diào)用時(shí),會先調(diào)用這個(gè)切面類的readOrWriteDB方法。在這里需要注意aop:aspect>中的order="1" 配置,主要是為了解決切面于切面之間的優(yōu)先級問題,因?yàn)檎麄€(gè)系統(tǒng)中不太可能只有一個(gè)切面類。

Spring AOP 切面類實(shí)現(xiàn)

public class ReadWriteInterceptor { 
  private static final String DB_SERVICE = "dbService"; 
  private ListString> readMethodList = new ArrayListString>(); 
  private ListString> writeMethodList = new ArrayListString>(); 
  public Object readOrWriteDB(ProceedingJoinPoint pjp) throws Throwable { 
    String methodName = pjp.getSignature().getName(); 
    if (isChooseReadDB(methodName)) { 
      //選擇slave數(shù)據(jù)源 
    } else if (isChooseWriteDB(methodName)) { 
      //選擇master數(shù)據(jù)源 
    } else { 
     //選擇master數(shù)據(jù)源 
    } 
    return pjp.proceed(); 
} 
 
 private boolean isChooseWriteDB(String methodName) { 
   for (String mappedName : this.writeMethodList) { 
     if (isMatch(methodName, mappedName)) { 
       return true; 
     } 
   } 
  return false; 
} 
 
 private boolean isChooseReadDB(String methodName) { 
  for (String mappedName : this.readMethodList) { 
    if (isMatch(methodName, mappedName)) { 
      return true; 
    } 
  } 
  return false; 
} 
 
 private boolean isMatch(String methodName, String mappedName) { 
  return PatternMatchUtils.simpleMatch(mappedName, methodName); 
} 
 
 public ListString> getReadMethodList() { 
  return readMethodList; 
 } 
 
 public void setReadMethodList(ListString> readMethodList) { 
  this.readMethodList = readMethodList; 
} 
 
 public ListString> getWriteMethodList() { 
  return writeMethodList; 
 } 
 
 public void setWriteMethodList(ListString> writeMethodList) { 
  this.writeMethodList = writeMethodList; 
} 
 

覆蓋DynamicDataSource類中的getConnection方法

ReadWriteInterceptor中的readOrWriteDB方法只是決定選擇主還是從,我們還必須覆蓋數(shù)據(jù)源的getConnection方法,以便獲取正確的connection。一般來說,是一主多從,即一個(gè)master庫,多個(gè)slave庫的,所以還得解決多個(gè)slave庫之間負(fù)載均衡、故障轉(zhuǎn)移以及失敗重連接等問題。

1、負(fù)載均衡問題,slave不多,系統(tǒng)并發(fā)讀不高的話,直接使用隨機(jī)數(shù)訪問也是可以的。就是根據(jù)slave的臺數(shù),然后產(chǎn)生隨機(jī)數(shù),隨機(jī)的訪問slave。

2、故障轉(zhuǎn)移,如果發(fā)現(xiàn)connection獲取不到了,則把它從slave列表中移除,等其回復(fù)后,再加入到slave列表中

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

處理業(yè)務(wù)方法中的@Transactional注解

我參與的這個(gè)項(xiàng)目,大部分業(yè)務(wù)代碼是不需要事務(wù)的,只有極個(gè)別情況需要。那么按照上面提到的方案,如果不對業(yè)務(wù)方法中@Transactional注解進(jìn)行特殊處理的話,主從的選擇會出現(xiàn)問題。大家都知道,如果使用了Spring的事務(wù),那么在同一個(gè)業(yè)務(wù)方法內(nèi),只會調(diào)用一次數(shù)據(jù)源的getConnection方法,如果該業(yè)務(wù)方法內(nèi),調(diào)用的mapper接口剛好以select開頭的,就會選擇slave庫,那么接下來調(diào)用以insert開頭的mapper接口方法時(shí),會把數(shù)據(jù)寫入到slave庫。如何解決這個(gè)問題呢?必須在進(jìn)入標(biāo)有@Transactional注解的業(yè)務(wù)方法前,指定選擇master主庫??梢酝ㄟ^覆蓋DataSourceTransactionManager類中的doBegin方法,如下:

public class MyTransactionManager extendsDataSourceTransactionManager{ 
 
@Override 
 
protected void doBegin(Object transaction, TransactionDefinitiondefinition) { 
 
//選擇master數(shù)據(jù)庫 
 
super.doBegin(transaction, definition); 
 
} 
 
} 

這樣既可以避免,把數(shù)據(jù)寫入到從庫的問題。

總結(jié)

本人的解決方案是基于項(xiàng)目實(shí)際的,不一定合適你,我只是展示了解決方案而已。當(dāng)然你可以選擇開源的框架,像阿里的Cobar,360的Atlas。

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

您可能感興趣的文章:
  • Spring AOP面向切面編程實(shí)現(xiàn)原理方法詳解
  • 解決springboot的aop切面不起作用問題(失效的排查)
  • SpringBoot整合aop面向切面編程過程解析
  • springboot配置aop切面日志打印過程解析
  • Spring 面向切面編程AOP實(shí)現(xiàn)詳解
  • 詳解Spring AOP 實(shí)現(xiàn)“切面式”valid校驗(yàn)
  • 詳解spring面向切面aop攔截器
  • Spring AOP面向切面編程實(shí)現(xiàn)及配置詳解

標(biāo)簽:萍鄉(xiāng) 大理 衢州 蚌埠 棗莊 江蘇 廣元 衡水

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Spring AOP切面解決數(shù)據(jù)庫讀寫分離實(shí)例詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    武邑县| 灵璧县| 南康市| 于都县| 象山县| 肃北| 崇义县| 夹江县| 察哈| 新邵县| 铜鼓县| 寻甸| 利津县| 封开县| 泾阳县| 博兴县| 遂昌县| 长寿区| 河北省| 个旧市| 罗山县| 闸北区| 彭阳县| 旬阳县| 枣庄市| 大邑县| 旌德县| 沙洋县| 丰顺县| 南乐县| 那曲县| 永城市| 侯马市| 毕节市| 青海省| 仪征市| 荔波县| 乌什县| 仁怀市| 赣州市| 包头市|