垂直切分就是要把表按模塊劃分到不同數據庫中,這種拆分在大型網站的演變過程中是很常見的。當一個網站還在很小的時候,只有小量的人來開發和維護,各模塊和表都在一起,當網站不斷豐富和壯大的時候,也會變成多個子系統來支撐,這時就有按模塊和功能把表劃分出來的需求。如下圖所示:
其實,相對於垂直切分更進一步的是服務化改造,說得簡單就是要把原來強耦合的系統拆分成多個弱耦合的服務,通過服務間的調用來滿足業務需求看,因此表拆出來後要通過服務的形式暴露出去,而不是直接調用不同模塊的表,淘寶在架構不斷演變過程,最重要的一環就是服務化改造,把用戶、交易、店鋪、寶貝這些核心的概念抽取成獨立的服務,也非常有利於進行局部的優化和治理,保障核心模塊的穩定性。這樣一種拆分方式也是有代價的:
- 表關聯無法在數據庫層面做
- 單表大數據量依然存在性能瓶頸
- 事務保證比較複雜
- 應用端的複雜性增加
上面這些問題是顯而易見的,處理這些的關鍵在於如何解除不同模塊間的耦合性,這說是技術問題,其實更是業務的設計問題,只有在業務上是鬆耦合的,纔可能在技術設計上隔離開來。沒有耦合性,也就不存在表關聯和事務的需求。另外,大數據瓶頸問題可以採用水平切分。
二、對數據庫表的字段訪問比較均衡,業務導向不明顯(對單一應用的高併發訪問)
水平切分沒有破壞表之間的聯繫,完全可以把有關係的表放在一個庫裏,這樣就不影響應用端的業務需求,並且這樣的切分能從根本上解決大數據量的問題。它的問題也是很明顯的:
- 當切分規則複雜時,增加了應用端調用的難度
- 數據維護難度比較大,當拆分規則有變化時,需要對數據進行遷移
對於第一個問題,可以參考如何整合應用端和數據庫端。對於第二個問題可以參考一致性hash的算法,通過某些映射策略來降低數據維護的成本
2)當然還可以把水平切分和垂直切分結合起來
由上面可知垂直切分能更清晰化模塊劃分,區分治理,水平切分能解決大數據量性能瓶頸問題,因此常常就會把兩者結合使用,這在大型網站裏是種常見的策略,這可以結合兩者的優點,當然缺點就是比較複雜,成本較高,不太適合小型網站,下面是結合前面兩個例子的情況:
三、對數據庫表的單一字段訪問比較集中(秒殺、大量用戶對同一賬戶操作)
對於這種情況有很多種解決方案,但是每一種都不是很完美:
1)採用內存緩存或者緩存數據庫來緩解數據的庫的壓力
具體做法是:在利用內存緩存或者緩存數據庫把後臺數據庫服務器上相關的表數據加載到內存中,所用用戶高併發的對內存數據進行處理,然後再定時輪詢的方式把內存的數據刷新到後臺數據庫表中,這種做法有以下問題:
a)不能很好保持內存數據與數據庫數據的一致性;
b)如果出現斷電、內存損壞等情況,會有數據丟失;
2)採用對數據庫表水平切分,然後在後臺的程序中對各個表的數據整體控制
例如,有10000億人民幣爲1億人併發提供貸款業務。在數據庫中建立一個總表存下10000億人民幣,然後再建立10張分表,初始設爲空;後臺java程序在訪問數據庫時會有一個控制程序(中間件),開10個 線程池,每個線程池對應一個數據庫分表,當中間件接受到貸款申請時,中間件就會根據用戶的ID(可以ip地址,賬戶編號)hash到相應的線程去到總表中借款,這個借款數目可以根據總表的資金和用戶的要借的資金去申請額度(比如用戶申請10w,總表有1000億,對應線程可以向總表申請10億),存入相應分表,供這個用戶提供貸款,如果再有下個用戶再到此線程池操作數據庫表,就直接操作,分表中金額不夠的時候再到總表中借款。
這樣的設計解決了,高併發存儲數據庫的問題,但是增加了後臺的程序設計的難度,加大了程序的耦合度。
3)採用“記流水不記賬“的方式應對
還用上一個例子,這種方式,需要在數據庫中設計兩個表,一個用來存儲賬戶金額(賬戶表),另一個記錄”流水“(流水錶), 所謂”記流水“是指每當有個請求到來,就向流水錶中插入一條記錄,然後定時對所插入的記錄進行統計,update賬戶表的數據,當然這種方式,需要在內存中增加變量,來控制所用用戶的貸款不能超過所貸款的總金額。這種處理方式是數據庫端處理秒殺、高併發集中訪問數據庫表字段的有效方式,使用比較廣泛。
4)針對網購秒殺還有其針對性的設計,因爲網購秒殺和高併發操作銀行賬戶不同,網購秒殺允許用戶請求丟失,簡單的來說,只需要在內存緩存或者內存數據庫(充當隊列)中保存較早的用戶請求,然後再異步的處理這些請求來操作數據庫(更新數據庫)。