深入理解java—分庫分表如何解決跨庫查詢等問題

Java 編程語言 程序員 MySQL java飛虎隊 java飛虎隊 2017-10-01

垂直分庫

垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模塊來劃分出不同的數據庫,而不是像早期一樣將所有的數據表都放到同一個數據庫中。如下圖:

深入理解java—分庫分表如何解決跨庫查詢等問題

垂直分庫

系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利於系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。

眾所周知,數據庫往往最容易成為應用系統的瓶頸,而數據庫本身屬於“有狀態”的,相對於Web和應用服務器來講,是比較難實現“橫向擴展”的。數據庫的連接資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬件資源的瓶頸,是大型分佈式系統中優化數據庫架構的重要手段。

垂直分表

垂直分表在日常開發和設計中比較常見,通俗的說法叫做“大表拆小表”,拆分是基於關係型數據庫中的“列”(字段)進行的。通常情況,某個表中的字段比較多,可以新建立一張“擴展表”,將不經常使用或者長度較大的字段拆分出去放到“擴展表”中,如下圖所示:

深入理解java—分庫分表如何解決跨庫查詢等問題

垂直分表

在字段很多的情況下,拆分開確實更便於開發和維護(筆者曾見過某個遺留系統中,一個大表中包含100多列的)。某種意義上也能避免“跨頁”的問題(MySQL、MSSQL底層都是通過“數據頁”來存儲的,“跨頁”問題可能會造成額外的性能開銷,這裡不展開,感興趣的朋友可以自行查閱相關資料進行研究)。

水平分表

水平分表也稱為橫向分表,比較容易理解,就是將表中不同的數據行按照一定規律分佈到不同的數據庫表中(這些表保存在同一個數據庫中),這樣來降低單表數據量,優化查詢性能。最常見的方式就是通過主鍵或者時間等字段進行Hash和取模後拆分。如下圖所示:

深入理解java—分庫分表如何解決跨庫查詢等問題

水平分表

水平分表,能夠降低單表的數據量,一定程度上可以緩解查詢性能瓶頸。但本質上這些表還保存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議採用這種做法。

水平分庫分表

水平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表保存在不同的數據中。這也是很多大型互聯網公司所選擇的做法。如下圖:

深入理解java—分庫分表如何解決跨庫查詢等問題

水平分庫分表

某種意義上來講,有些系統中使用的“冷熱數據分離”(將一些使用較少的歷史數據遷移到其他的數據庫中。而在業務功能上,通常默認只提供熱點數據的查詢),也是類似的實踐。在高併發和海量數據的場景下,分庫分表能夠有效緩解單機和單庫的性能瓶頸和壓力,突破IO、連接數、硬件資源的瓶頸。當然,投入的硬件成本也會更高。同時,這也會帶來一些複雜的技術問題和挑戰(例如:跨分片的複雜查詢,跨分片事務等)

全局表

所謂全局表,就是有可能系統中所有模塊都可能會依賴到的一些表。比較類似我們理解的“數據字典”。為了避免跨庫join查詢,我們可以將這類表在其他每個數據庫中均保存一份。同時,這類數據通常也很少發生修改(甚至幾乎不會),所以也不用太擔心“一致性”問題。

字段冗餘

這是一種典型的反範式設計,在互聯網行業中比較常見,通常是為了性能來避免join查詢。

舉個電商業務中很簡單的場景:

“訂單表”中保存“賣家Id”的同時,將賣家的“Name”字段也冗餘,這樣查詢訂單詳情的時候就不需要再去查詢“賣家用戶表”。

字段冗餘能帶來便利,是一種“空間換時間”的體現。但其適用場景也比較有限,比較適合依賴字段較少的情況。最複雜的還是數據一致性問題,這點很難保證,可以藉助數據庫中的觸發器或者在業務代碼層面去保證。當然,也需要結合實際業務場景來看一致性的要求。就像上面例子,如果賣家修改了Name之後,是否需要在訂單信息中同步更新呢?

數據同步

定時A庫中的tab_a表和B庫中tbl_b有關聯,可以定時將指定的表做同步。當然,同步本來會對數據庫帶來一定的影響,需要性能影響和數據時效性中取得一個平衡。這樣來避免複雜的跨庫查詢。筆者曾經在項目中是通過ETL工具來實施的。

系統層組裝

在系統層面,通過調用不同模塊的組件或者服務,獲取到數據並進行字段拼裝。說起來很容易,但實踐起來可真沒有這麼簡單,尤其是數據庫設計上存在問題但又無法輕易調整的時候。

深入理解java—分庫分表如何解決跨庫查詢等問題

簡單的列表查詢的情況

偽代碼很容易理解,先獲取“我的提問列表”數據,然後再根據列表中的UserId去循環調用依賴的用戶服務獲取到用戶的RealName,拼裝結果並返回。

有經驗的讀者一眼就能看出上訴偽代碼存在效率問題。循環調用服務,可能會有循環RPC,循環查詢數據庫…不推薦使用。再看看改進後的:

深入理解java—分庫分表如何解決跨庫查詢等問題

這種實現方式,看起來要優雅一點,其實就是把循環調用改成一次調用。當然,用戶服務的數據庫查詢中很可能是In查詢,效率方面比上一種方式更高。(坊間流傳In查詢會全表掃描,存在性能問題,傳聞不可全信。其實查詢優化器都是基本成本估算的,經過測試,在In語句中條件字段有索引的時候,條件較少的情況是會走索引的。

簡單字段組裝的情況下,我們只需要先獲取“主表”數據,然後再根據關聯關係,調用其他模塊的組件或服務來獲取依賴的其他字段(如例中依賴的用戶信息),最後將數據進行組裝。

垂直分庫總結和實踐建議

本篇中主要描述了幾種常見的拆分方式,並著重介紹了垂直分庫帶來的一些問題和解決思路。讀者朋友可能還有些問題和疑惑。

我們目前的數據庫是否需要進行垂直分庫?

根據系統架構和公司實際情況來,如果你們的系統還是個簡單的單體應用,並且沒有什麼訪問量和數據量,那就彆著急折騰“垂直分庫”了,否則沒有任何收益,也很難有好結果。

切記,“過度設計”和“過早優化”是很多架構師和技術人員常犯的毛病。

垂直拆分有沒有原則或者技巧?

沒有什麼黃金法則和標準答案。一般是參考系統的業務模塊拆分來進行數據庫的拆分。比如“用戶服務”,對應的可能就是“用戶數據庫”。但是也不一定嚴格一一對應。有些情況下,數據庫拆分的粒度可能會比系統拆分的粒度更粗。筆者也確實見過有些系統中的某些表原本應該放A庫中的,卻放在了B庫中。有些庫和表原本是可以合併的,卻單獨保存著。還有些表,看起來放在A庫中也OK,放在B庫中也合理。

總結

到這裡,分庫分表如何解決跨庫查詢等問題就結束了,,不足之處還望大家多多包涵!!覺得收穫的話可以點個關注收藏轉發一波喔,謝謝大佬們支持。(吹一波,233~~)

下面和大家交流幾點編程的經驗:

1、多寫多敲代碼,好的代碼與紮實的基礎知識一定是實踐出來的

2丶 測試、測試再測試,如果你不徹底測試自己的代碼,那恐怕你開發的就不只是代碼,可能還會聲名狼藉。

3丶 簡化算法,代碼如惡魔,在你完成編碼後,應回頭並且優化它。從長遠來看,這裡或那裡一些的改進,會讓後來的支持人員更加輕鬆。

4、可以去騰訊課堂的圖靈學院學習一下java架構實戰案例,還挺不錯的。

最後,每一位讀到這裡的網友,感謝你們能耐心地看完。希望在成為一名更優秀的Java程序員的道路上,我們可以一起學習、一起進步。

內部交流群469717771 歡迎各位前來交流和分享, 驗證:(009)必過!!

深入理解java—分庫分表如何解決跨庫查詢等問題

相關推薦

推薦中...