做完橫向拆分后,每一個服務(wù)其實都對應(yīng)了一個存儲矩陣,服務(wù)中每一個業(yè)務(wù)處理模塊都對應(yīng)幾個數(shù)據(jù)庫,這幾個數(shù)據(jù)庫是水平切分的。最簡單的水平切分規(guī)則是按主表主鍵大小,比如前100萬條數(shù)據(jù)存A1數(shù)據(jù)庫,100萬-200萬存A2數(shù)據(jù)庫,200萬-300萬存A3數(shù)據(jù)庫等。
數(shù)據(jù)庫存儲集群的方案目前業(yè)界有很多。關(guān)于關(guān)系型數(shù)據(jù)庫和NoSQL使用場景也是爭論不休。我們認(rèn)為,如果是事務(wù)性的應(yīng)用必須使用關(guān)系型數(shù)據(jù)庫,比如電子商務(wù)和互聯(lián)網(wǎng)金融,事務(wù)性比較弱的可以采用NoSQL,比如博客、新聞等應(yīng)用,事務(wù)性可以作為是否選用NoSQL數(shù)據(jù)庫的標(biāo)準(zhǔn)。即便是關(guān)系型數(shù)據(jù)庫集群在編程方面也存在多種方案,比如:
這個技術(shù)都各有優(yōu)缺點,有的不支持本地數(shù)據(jù)庫級事務(wù),像Amoeba;有的支持?jǐn)?shù)據(jù)庫本地事務(wù)但又不支持跨庫情況下的Join、分頁、排序、子查詢,像Cobar。從本質(zhì)上說,數(shù)據(jù)存儲也是業(yè)務(wù)處理的一部分,舉個簡單的例子,還是那個轉(zhuǎn)賬的例子:A在Web頁面提交一個轉(zhuǎn)賬給B申請,業(yè)務(wù)處理模塊拿到轉(zhuǎn)賬申請從A賬戶扣除轉(zhuǎn)賬金額(數(shù)據(jù)庫操作)并給B的余額加上轉(zhuǎn)賬金額(數(shù)據(jù)庫操作),并記錄操作日志和通知B。很明顯,數(shù)據(jù)庫操作是這個轉(zhuǎn)賬業(yè)務(wù)處理操作的一部分,所以如果在業(yè)務(wù)層直接進(jìn)行分布式存儲和按需存儲,事情將變得簡單的多(spring提倡的鏈?zhǔn)绞聞?wù)管理就是這種方式)。如果你不把數(shù)據(jù)庫存儲作為業(yè)務(wù)層處理的一部分,而封裝成一個獨立運(yùn)行的模塊,那么就會出現(xiàn)各種各樣的問題。
如果將數(shù)據(jù)庫路由鍵放入服務(wù)層,一個典型業(yè)務(wù)層接口類定義如下:
public interface SporeService {
public List<Spore> searchByCondition(RouteKey routeKey);
public Spore findById(Long id,RouteKey routeKey);
public void update(Spore spore,RouteKey routeKey);
public void insert(Spore spore,RouteKey routeKey);
public void delete(Long id,RouteKey routeKey);
}
這是業(yè)務(wù)層定義的接口,在這里增加了一個數(shù)據(jù)路由的key,為什么把數(shù)據(jù)路由的選擇放到業(yè)務(wù)層接口,我們前面提到過,數(shù)據(jù)訪問本身屬于業(yè)務(wù)的一種操作。就比如你今天去A銀行取錢和去B銀行取錢完全是兩種業(yè)務(wù)。數(shù)據(jù)路由鍵讓每一個業(yè)務(wù)方法都可以對應(yīng)一個數(shù)據(jù)庫矩陣,而key就是該業(yè)務(wù)方法所需數(shù)據(jù)庫的選擇方法??赡苡械某绦騿T認(rèn)為在每個方法增加一個RouteKey routeKey參數(shù)太過于繁瑣,是不是可以定義成繼承關(guān)系或注解等等,那相比這種方式更加繁瑣,而且不適合于所有情況。