天天看點

時序資料從分表到分庫

這裡的時序資料泛指一切随時間推移而不斷增長的資料,比如通話記錄、銀行交易記錄等。

對于資料庫來講,時序資料并沒有什麼特殊性,可以和普通資料一樣放在資料表中。不過,因為不斷增長,積累時間較長後,這種資料的量常常都會很大。一個實體表的資料量太大時,就會影響查詢和計算的性能。

現代資料庫一般都提供有表分區(PARTITION)的機制,就是把一個大表縱向(按行)分成若幹區段,分區規則由資料庫管理者來設定,對應用程式員來講是透明的,可以和不分區的表一樣通路,資料庫會自動根據查詢條件決定讀取哪些分區的資料,這樣的接口體驗非常好。

不過,在實戰中,分區表的效果在某些場景下并不好,而且使用時也有些限制條件,并不總好用且能用的。結果,在實際業務中,我們常常會看到對于這種大資料采用手工實體分表的方案。

所謂實體分表,就是人為将一個大表分成若幹較小的實體資料表。因為時序資料的結構中一定會有一個字段來表示事件發生的時刻,而事件發生的數量一般來講也會按時間段相對平均分布(大多數情況會緩慢增長,但讨論時可以忽略),是以最常用的方案就是按時間段來做分表,比如一個月資料對應一個分表,這種方式在金融、電信行業比較普遍。

實體分表并不是資料庫自動支援的方案,不能對應用程式做到透明,需要應用程式自己處理。在查詢資料時一般都會有時間段參數,應用程式可以根據這個參數計算出該查詢涉及哪些分表,然後将這些分表UNION起來拼到SQL語句的FROM後面。查詢不涉及的時間段對應的分表不會被拼進來,這樣就可以有效減少資料周遊的範圍,進而提高性能。

這個方案在單個資料庫時沒啥毛病,但是不是能推廣到多個資料庫的情況呢?

資料量再大下去,一個資料庫也無法承受了,而某些場景下又不允許我們上一套分布式資料庫系統,畢竟分布式資料庫是個沉重的工程,不僅造價高,而且維護管理都要複雜不少。這時候,我們可以擺多個資料庫分别存儲資料,類似實體分表的方案,也按時間段把資料分拆到各個資料庫中,比如一年資料放入一個資料庫中(一般來講多個庫會部署到多台機器上),這樣就能分攤查詢壓力了。

這首先會有一個查詢範圍的問題,如果查詢的時間跨度超過了一個實體分庫時,這時候就不能象分表時那樣用UNION拼起來了,資料庫無法執行跨庫的SQL語句。不過,這個問題還不算嚴重,隻是查詢明細資料時,要把各個分庫的傳回資料拼接起來,這并不算困難。甚至,要求前端查詢範圍必須落在一個分庫内也不為過(比如必須先選擇查詢年份),因為一個分庫的資料量并不算少,這樣使用者體驗略有損失,但也可以容忍。

這種方案還會有壓力不平衡的問題。

對于時序資料,近期資料的查詢頻繁度遠遠高于遠期資料,大多數查詢都集中在最近一段時間中,存放近期資料的分庫上任務就很重,并發較多時仍然會有性能瓶頸,而存放遠期資料的分庫卻幾乎沒事幹,并不能有效分攤查詢壓力。

還有别的辦法嗎?

可以采用蛇形分布。比如将多年資料分拆到10個分庫中,可以按日期拆分,所有年份中1月1日的資料放到1号分庫中,1月2日的放到2号分庫,…,1月10号的放到10号分庫,1月11号的再從1号分庫輪回,…;其它情況的具體分法也可以根據時序資料的時刻字段的分布情況來決定。

這樣分下來,每個分庫存儲的資料量差不多也就是1/n,相對比較平均,還可以規避前面說的資料緩慢增長導緻的不平衡;而且,無論近期資料還是遠期資料的查詢都會被分攤到各個分庫中,看起來能夠充分利用硬體資源了。

還有點注意事項!

蛇形分布時,每個分庫中都有所有年份的資料,幾乎每個查詢都會涉及到所有分庫的資料,不能隻挑出某些分庫來執行運算,這和前面說的分表方案的優化原理并不一樣了。我們需要在分庫中繼續做分表,查詢确實會涉及所有分庫,但隻涉及分庫中的某些分表,這樣仍然可以有效的減少查詢範圍,同時利用分庫并行的優勢。

第二個問題:每個分庫都可能傳回資料,應用程式需要把這些資料再做一次彙總,而不能象單庫分表那樣用UNION推給資料庫去完成。對于常見的明細查詢,那隻要簡單拼接再排序就可以了,開發起來并不難;但如果涉及到分組彙總就會麻煩很多,應用程式員并不擅長編寫這種運算,這時候最好借助集算器這類外部計算引擎來協助實作跨庫彙總運算。

當然,成本和條件允許時直接上分布式資料庫就更簡單,分布式資料庫采用HASH方案基本上可以被了解成是蛇形分布的。

原文釋出時間為:2018-10-30

本文作者:蔣步星

本文來自雲栖社群合作夥伴“

資料蔣堂

”,了解相關資訊可以關注“

”。