在互聯網時代,隨著業務數量的暴增和應用規模的不斷擴大,無論是oracle還是mysql這樣子的關系型數據庫,都會面臨服務器CPU、磁盤IO和內存的各種瓶頸問題?;诖饲闆r,各個業務團隊迫切需要一種數據分片的方案將業務數據量存儲成本分攤到成本可控的各個普通數據庫服務器上,數據庫切分的方案便應運而生。
本文將結合實際的業務場景進行分析(Java自學網推薦),詳細闡述選型開源組件—ShardingJdbc時候的一些思考,并最終給讀者呈現業務系統集成ShardingJdbc的最終設計方案。另外,本文僅為使用開源組件ShardingJdbc第一篇幅,后續篇幅還將繼續介紹開源組件ShardingJdbc的一些其他進階用法。
數據的切分方式
一般,線上系統的業務量不是很大,比如說單庫的數據量在百萬級別以下,那么MySQL的單庫即可完成任何增/刪/改/查的業務操作。隨著業務的發展,單個DB中保存的數據量(用戶、訂單、計費明細和權限規則等數據)呈現指數級增長,那么各種業務處理操作都會面臨單DB的IO讀寫瓶頸帶來的性能問題。
垂直切分方案
這時候,我們會考慮使用對之前的整個單DB采用垂直數據切分的方案,根據不同的業務類型劃分庫表,比如訂單相關若干表放在訂單庫,用戶相關的表放在用戶庫,賬務明細相關的表放在賬務庫等。這樣就將數據分擔到了不同的庫上,達到專庫專用的效果。
使用垂直切分方案的主要優點如下:
a.拆分后業務清晰,符合微服務的總體設計理念;
b.子系統之間的整合與擴展相對容易;
c.按照不同的業務類型,將不同的庫表放在不同的數據庫服務器上,便于管理;
d.根據業務數據的“冷”、“熱”狀態,采用不同的緩存和數據庫模式設計方案;
垂直切分的缺點如下:
a.跨庫的Join查詢,需要使用不同子系統的API接口讀取后在內存中完成關聯查詢,提高系統復雜度;
b.如果某一種類型的業務呈現爆發式地增長,該業務對應的庫表還是會存在單DB的IO讀寫的瓶頸問題,從這一點來說垂直切分并未從根本解決單庫單表數據量過大的問題;
c.存在跨庫事務的一致性問題,解決起來比較麻煩,當然也可以采用分布式事務來解決;
水平切分方案
由于本文主題講的是利用開源組件ShardingJdbc進行數據水平切分的實踐,因此對于垂直切分方案的一些細節問題就不做過多的詳細介紹了。與垂直切分對比,這里講的水平切分不是將庫表根據業務類型進行分類存儲,而是將其按照數據表中某個字段或某幾個字段的某種規則切分存儲至多個DB中,在每個庫每個表中所包含的只是其中的一部分數據,所有庫表加起來的才是全量的業務數據。
這種對數據的切分方式,基本可以保證經過水平切分后的單庫單表存儲的容量不會太大,從而保證了對單表的增/刪/改/查的SQL執行效率和處理能力。其中,數據的分片規則也是需要重點考慮的,因為它會使得數據分片在多個庫表中是否均勻分布存儲。然而,數據水平切分方案為業務系統帶來福音的同時也給系統構建帶來了一些值得思考的問題。
使用水平切分方案的主要優點如下:
a.單庫單表的數據容量可以維持在一個量級,有助于提高業務SQL的執行效率和系統性能;
b.不管是利用ShardingJdbc組件還是MyCat框架,業務系統的應用層涉及改造得都較少,只需要根據實際的業務情況來設計數據分片的路由規則即可;
c.可以提高業務系統的穩定性和負載能力;
使用水平切分方案的主要缺點如下:
a.數據水平切分后,分布在多庫多表中,跨庫Join查詢比較復雜;
b.分片數據的一致性較為難保證;
c.對于歷史數據的遷移和數據庫的擴容需要較大的維護工作量;
選型ShardingJdbc組件的分析
對于,“流水”/“明細”一類的業務數據,通常的業務需求是準實時或者說相對滯后,這些數據是按小時、按日和按月匯總加工處理后生成最終業務需求的數據(比如用戶賬單、報表和話單)。此外,由于這一類型的業務數據量普遍較大,比如清算系統的清分明細、云管平臺的資源計量明細、訂單系統的訂單流水和云計算主機資源上報的性能數據等,如果只是使用單庫單表存儲的普通方案,那么在單表數據量達到千萬級別以上的時候,單庫的IO讀寫能力將持續降低,會成為業務系統整體吞吐量和性能的瓶頸。
對于上述的問題,有一些對DB較為熟悉的同學第一時間想到的解決方案,可能會是MySQL的分區表。MySQL的分區表比較適合用于解決業務數據具有較強時間序列特點,且數據量偏大的場景。但是,如果SQL的查詢條件并非基于時間維度的,那么執行起來的效率并不會有所改觀,同時對于處理單表千萬級別以上數據量時,MySQL的分區表方案還是會顯得有些“力不從心”。
對于以上這種“流水”/“明細”類的業務數據,作者還是推薦使用水平切分的方案從根本上來解決業務上遇到的單表數據過大的問題。由于該類型的業務數據基本不會涉及跨庫的Join連表SQL查詢、只需保證分庫的本地事務,且并不會遇到上面水平切分方案中的幾個需要考慮的問題,對于這樣子的業務場景可以考慮使用水平切分的方案。那么,我們應該選擇哪一款開源的分庫分表組件,又或者是根據業務情況來自己定制化研發呢?
選型分庫分表中間件的分析
如果業務系統設計之初打算采用水平切分方案的話,那么有必要好好思考下如何來選型分庫分表的中間件。在當前業界開源組件中比較流行的兩款代表分別為ShardingJdbc和MyCat。
ShardingJdbc這款分庫分表組件代表了客戶端類的分庫分表技術框架,其定位為輕量級數據庫驅動,可以由Spring Boot這樣的業務工程直接快速集成,以jar包形式提供服務,無需額外部署和其他依賴。對于業務系統的開發人員與數據庫運維人員無需改變原有的開發與運維方式。在2.X版本中,采用"半理解"理念的SQL解析引擎,以達到性能與兼容性的最大平衡。因此,ShardingJdbc即為增強版的JDBC驅動,其優勢在于無需對原有的業務工程進行任何改造的基礎上,即可使其擁有分庫分表的能力,成本較低,易于上手。但是,也有缺點,中間件與業務應用工程綁定在一起,對應用本身有侵入。并且目前只支持Java語言,問題難以追蹤。
而MyCat是一個開源的分布式數據庫系統(屬于代理類框架),相當于一個實現了MySQL協議的數據庫代理服務器。用戶可以使用MySQL客戶端工具和命令行訪問,而其后端使用MySQL原生協議與多個MySQL數據庫服務器通信,也可以用JDBC協議與大多數主流數據庫服務器通信。其分庫分表的方案優點在于,能夠處理非常復雜的需求,不受數據庫訪問層原來實現的限制,擴展性強且對于應用服務透明且沒有增加任何額外負載。其缺點是上線部署需要額外的中間件,增加運維成本;應用服務需經過代理來連接數據庫,網絡上多了一層鏈接的開銷,性能有損失且有額外風險。
使用ShardingJdbc解決的基本業務場景
選擇ShardingJdbc組件后,就需要使用該組件來解決實際的問題。這一節主要根據之前提到的“流水”/“明細”一類的業務數據,同時結合ShardingJdbc組件的特點來進行一定的分析,使得讀者對正確使用ShardingJdbc組件進行業務系統水平切分有一定的了解。
前面已經提到了“流水”/ “明細”類的業務數據,一般是準實時或者說相對滯后,需要按小時、按日和按月匯總處理后生成最終的業務數據(如賬單、報表和話單等)。我們對“流水”/“明細型”業務數據處理過程中,一般都會涉及數據落庫(Insert SQL)、數據分組匯總和分組查詢(Select+sum(xxx)+Group By SQL)以及刪除數據表(Delete SQL)等業務處理操作。這里有必要對這幾種類型的SQL進行一定的說明:
a. 數據落庫(Insert SQL):流水/明細類的數據一般都需要先DB持久化處理;以云計算平臺中10W臺虛擬機同時產生性能明細數據上報,如果是單庫單表,則這個數據庫會進行每小時會有10W次的寫請求;如果使用100個分表(分10個庫,每庫10個表)則每個表平均承受1000次寫請求,每個庫平均分擔1W次的請求壓力,這樣子就可以將原來單庫單表的寫請求壓力成倍的減少;一般業務研發人員使用ShardingJdbc組件,設置合理的數據分片路由規則,即可使得流水/明細類的數據基本均勻落在我們預先設置的分庫分表中。
b. 數據分組匯總查詢(Select+sum(xxx)+Group By SQL):由于(a)中持久化至分庫分表的業務數據為若干段時間的業務數據,根據業務需求還需要按日,按周或者按月進行累加匯總,因此有必要對各個分表中的數據執行Select+sum(xxx)+Group By的分組匯總SQL;ShardingJdbc組件可以完成SQL的解析、改寫、路由和結果歸并,對于“Select+sum(xxx)+Group By SQL”的語句,可以遍歷設置的多個分庫分表,對每個分庫分表執行SQL后進行一個結果歸并再返回給業務調用方。
c. 刪除數據表(Delete SQL):一般業務系統對會通過定時任務來生成明細數據加工處理后的業務數據(比如用戶賬單、清償明細、云資源按日按月的話單)。一旦生成這些有效業務數據后,原來落庫的明細也就沒有什么業務價值,可以通過任務定期刪除或者遷移至歷史庫的方式來使得分庫分表的數據水位量級維持在一定量,因此就需要涉及對原來存儲在分庫分表的明細數據進行刪除;ShardingJdbc組件可以根據“Delete SQL”語句中的篩選條件進行規則路由來定位某個分庫和分表,否則會刪除所有分庫中的分表數據。
系統集成ShardingJdbc的設計
根據上面對“流水”/“明細”型業務數據的場景分析,基本可以畫出SpringBoot業務工程集成分庫分表組件ShardingJdbc的架構設計圖:
從上面業務系統架構設計圖中可以看出,明細型業務數據(比如之前提的,清算系統的清分明細、云管平臺的資源計量明細、訂單系統的訂單流水和云計算主機資源上報的性能數據明細)根據設置的數據分片路由規則先落庫至shardingdb00~ shardingdb04五個分庫的對應分表中。然后,利用ShardingJdbc組件對分組匯總查詢SQL的解析、改寫、路由和歸并結果的能力,分別對五個庫中對應業務分表中的數據匯總累加求出每天/每月同一個用戶下的資源計費累加值。最后,將這些“加工”后的業務數據批量插入至共享庫share_db中,其他定時任務再從共享庫中讀取并生成最終形式的業務數據(比如,按月的賬單、話單或者性能計量值)。其中,對于異常情況(明細流水異常、匯總異常和系統異常等),需要將其保存至共享庫中的異常信息表中。另外,在明細落庫之前還需要考慮冪等前置校驗的問題。對于ShardingJdbc組件的分庫分表路由規則可以參照下圖:
從上面的分庫分表路由規則圖上可以看出,預先設置了通過客戶id來路由定位至分庫,通過用戶id來路由定位至分表。這里,由原來的單庫單表分成五個庫的多分表(每個庫中均有testmsgqueuebillrecord0~testmsgqueuebillrecord4五個分表,這里只是示例,在真實的業務場景下需要根據業務數據的總體容量來設定分庫分表的規模,究竟是分5個庫每個庫5表,還是分10個庫每個庫10表來滿足業務要求)。在一般情況下,如果執行的SQL為“select * from testmsgqueuebillrecord”就能借助ShardingJdbc組件來遍歷查完5個分庫中的testmsgqueuebillrecord0~4五個分表,無需我們自己來做分庫分表的遍歷查詢了。
總結
本文先介紹了目前兩種數據切分的主要方案(垂直切分和水平切分)及其優缺點。根據“流水”/“明細”類別的數據切分業務場景,闡述了業務系統設計之初選型分庫分表組件的分析,并介紹了如何利用ShardingJdbc來解決“數據落庫(Insert SQL)”、“數據分組匯總查詢(Select+sum(xxx)+Group By SQL)”和“刪除數據表(Delete SQL)”的幾種基本業務場景。最后,給出業務系統集成ShardingJdbc組件后的架構設計方案。本文僅僅使用了Sharding-Jdbc組件的核心功能來解決了一部分基本的業務場景問題。ShardingJdbc組件還有其他很多高級的功能(比如讀寫分離、最大努力送達型的柔性事務、分片路由規則動態配置和數據庫治理和ShardingProxy等。P.S : 聽@亮哥說,ShardingJdbc 3.0起可以完美支持Batch Insert SQL,很是期待),限于篇幅將在后續的文章中結合對應的場景進行介紹。限于筆者的才疏學淺,對本文內容可能還有理解不到位的地方,如有闡述不合理之處還望留言一起探討。