天天看點

Spring 實作資料庫讀寫分離

現在大型的電子商務系統,在資料庫層面大都采用讀寫分離技術,就是一個master資料庫,多個slave資料庫。master庫負責資料更新和實時資料查詢,slave庫當然負責非實時資料查詢。因為在實際的應用中,資料庫都是讀多寫少(讀取資料的頻率高,更新資料的頻率相對較少),而讀取資料通常耗時比較長,占用資料庫伺服器的cpu較多,進而影響使用者體驗。我們通常的做法就是把查詢從主庫中抽取出來,采用多個從庫,使用負載均衡,減輕每個從庫的查詢壓力。

  采用讀寫分離技術的目标:有效減輕master庫的壓力,又可以把使用者查詢資料的請求分發到不同的slave庫,進而保證系統的健壯性。我們看下采用讀寫分離的背景。

  随着網站的業務不斷擴充,資料不斷增加,使用者越來越多,資料庫的壓力也就越來越大,采用傳統的方式,比如:資料庫或者sql的優化基本已達不到要求,這個時候可以采用讀寫分離的策 略來改變現狀。

  具體到開發中,如何友善的實作讀寫分離呢?目前常用的有兩種方式:

  1 第一種方式是我們最常用的方式,就是定義2個資料庫連接配接,一個是masterdatasource,另一個是slavedatasource。更新資料時我們讀取masterdatasource,查詢資料時我們讀取slavedatasource。這種方式很簡單,我就不贅述了。

  2 第二種方式動态資料源切換,就是在程式運作時,把資料源動态織入到程式中,進而選擇讀取主庫還是從庫。主要使用的技術是:annotation,spring aop ,反射。下面會詳細的介紹實作方式。

   在介紹實作方式之前,我們先準備一些必要的知識,spring 的abstractroutingdatasource 類

     abstractroutingdatasource這個類 是spring2.0以後增加的,我們先來看下abstractroutingdatasource的定義:

    public abstract class abstractroutingdatasource extends abstractdatasource implements initializingbean  {}

    abstractroutingdatasource繼承了abstractdatasource ,而abstractdatasource 又是datasource

的子類。datasource   是javax.sql 的資料源接口,定義如下:

  datasource 接口定義了2個方法,都是擷取資料庫連接配接。我們在看下abstractroutingdatasource 如何實作了datasource接口:

  很顯然就是調用自己的determinetargetdatasource()  方法擷取到connection。determinetargetdatasource方法定義如下:

   我們最關心的還是下面2句話:

    object lookupkey = determinecurrentlookupkey();

        datasource datasource = this.resolveddatasources.get(lookupkey);

    determinecurrentlookupkey方法傳回lookupkey,resolveddatasources方法就是根據lookupkey從map中獲得資料源。resolveddatasources 和determinecurrentlookupkey定義如下:

  private map<object, datasource> resolveddatasources;

  protected abstract object determinecurrentlookupkey()

  看到以上定義,我們是不是有點思路了,resolveddatasources是map類型,我們可以把masterdatasource和slavedatasource存到map中,如下:

    key        value

    master             masterdatasource

    slave                  slavedatasource

  我們在寫一個類dynamicdatasource  繼承abstractroutingdatasource,實作其determinecurrentlookupkey() 方法,該方法傳回map的key,master或slave。

  好了,說了這麼多,有點煩了,下面我們看下怎麼實作。

   上面已經提到了我們要使用的技術,我們先看下annotation的定義:

    我們還需要實作spring的抽象類abstractroutingdatasource,就是實作determinecurrentlookupkey方法:

    從dynamicdatasource 的定義看出,他傳回的是dynamicdatasourceholder.getdatasouce()值,我們需要在程式運作時調用dynamicdatasourceholder.putdatasource()方法,對其指派。下面是我們實作的核心部分,也就是aop部分,datasourceaspect定義如下:

    為了友善測試,我定義了2個資料庫,shop模拟master庫,test模拟slave庫,shop和test的表結構一緻,但資料不同,資料庫配置如下:

  在spring的配置中增加aop配置

   下面是mybatis的usermapper的定義,為了友善測試,登入讀取的是master庫,使用者清單讀取slave庫:

   好了,運作我們的eclipse看看效果,輸入使用者名admin 登入看看效果

  

Spring 實作資料庫讀寫分離
Spring 實作資料庫讀寫分離

   從圖中可以看出,登入的使用者和使用者清單的資料是不同的,也驗證了我們的實作,登入讀取master庫,使用者清單讀取slave庫。