采用讀寫分離技術的目标:有效減輕master庫的壓力,又可以把使用者查詢資料的請求分發到不同的slave庫,進而保證系統的健壯性。我們看下采用讀寫分離的背景。
随着網站的業務不斷擴充,資料不斷增加,使用者越來越多,資料庫的壓力也就越來越大,采用傳統的方式,比如:資料庫或者sql的優化基本已達不到要求,這個時候可以采用讀寫分離的策 略來改變現狀。
具體到開發中,如何友善的實作讀寫分離呢?目前常用的有兩種方式:
1 第一種方式是我們最常用的方式,就是定義2個資料庫連接配接,一個是masterdatasource,另一個是slavedatasource。更新資料時我們讀取masterdatasource,查詢資料時我們讀取slavedatasource。這種方式很簡單,我就不贅述了。
,反射。下面會詳細的介紹實作方式。
在介紹實作方式之前,我們先準備一些必要的知識,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 登入看看效果
從圖中可以看出,登入的使用者和使用者清單的資料是不同的,也驗證了我們的實作,登入讀取master庫,使用者清單讀取slave庫。
首先說明,本文的配置使用的最直接的方式,實際用起來可能會很麻煩。
實際應用中可能存在多種結合的情況,你可以了解本文的含義,不要死闆的使用。
針對這種情況,一個更好的解決方法可以參考(本人沒有實際嘗試過):
<a target="_blank" href="http://blog.csdn.net/lixiucheng005/article/details/17391857">http://blog.csdn.net/lixiucheng005/article/details/17391857</a>
還有一個通過spring<code>abstractroutingdatasource</code>路由接口的方式:
<a target="_blank" href="http://blog.csdn.net/xtj332/article/details/43953699">http://blog.csdn.net/xtj332/article/details/43953699</a>
這種情況可以參考本文,但是需要注意每一個資料庫對應的mapper要在不同的包下友善區分和配置。
另外分庫的情況下也會存在主從的情況,如果你的資料庫從庫過多,就參考上面提供的方法,或者尋找其他方式解決。
分庫的情況下,不同的資料庫的mapper一定放在不同的包下。
主從的情況下,同一個mapper會同時存在讀寫的情況,建立兩個并不合适,使用同一個即可。但是這種情況下需要注意,spring對mapper自動生成的名字是相同的,而且類型也相同,這是就不能直接注入<code>mapper</code>接口。需要通過<code>sqlsession</code>來解決。
這個檔案,主要是引入了<code>spring-datasource-master.xml</code>和<code>spring-datasource-slave.xml</code>。
和<code>master</code>差別不大,主要是<code>id</code>名字和資料源配置有差別。
這裡需要注意<code><tx:method name="*" read-only="true"/></code>是隻讀的。如果不是從庫,可以按主庫進行配置。
在下面代碼中:
必須通過<code>sqlsessionfactorybeanname</code>來指定不同的<code>sqlsessionfactory</code>。
這裡是針對主從的情況進行設定的,兩個配置掃描的<code>mapper</code>是一樣的,是以沒法直接注入,需要通過下面的麻煩方式注入。
因為<code>sqlsession</code>能通過<code>name</code>區分開,是以這裡從<code>sqlsession</code>擷取<code>mapper</code>。
另外如果需要考慮在同一個事務中寫讀的時候,需要使用相同的<code>writemapper</code>,這樣在讀的時候,才能擷取事務中的最新資料。
以上是主從的情況。
在分庫的情況時,由于不同mapper在不同的包下,是以可以直接使用<code>@resource</code>或者<code>@autowired</code>注入<code>mapper</code>,不需要通過<code>sqlsession</code>擷取。