常用的資料庫表水平拆分方案

2021-09-25 17:55:00 字數 2592 閱讀 9651

一,使用者中心,以使用者資料為例

user(uid, login_name, passwd, ***, age, nickname, …)

其中uid為主鍵id,其它欄位為使用者屬性

此方案架構在業務初期單表單庫能夠搞定,但是隨著業務量的迅速增長,資料量越來越大時,這時候就需要對資料庫進行水平拆分了,常見的水平切分演算法有「範圍法」和「雜湊法」。

1,範圍發:以使用者的uid主鍵為範圍規則劃分

•user-db0:儲存0到1千萬的uid資料

•user-db1:儲存1到2千萬的uid資料

範圍法的優點:

•切分簡單,根據uid,按照範圍,很快能夠定位到資料在哪個庫上

•擴容簡單,如果容量不夠,只要增加user-db2即可

範圍法的不足:

•uid必須要滿足遞增的特性

•資料量不均,新增的user-db2,在初期的資料會比較少

•請求量不均,一般來說,新註冊的使用者活躍度會比較高,故user-db1往往會比user-db0負載要高,導致伺服器利用率不平衡

2,雜湊法:以使用者的uid進行劃分

•user-db0:uid %2=0 的資料儲存在db0庫上

•user-db1:uid %2=1 的資料儲存在db1庫上

雜湊法的優點:

•切分策略簡單,根據uid取模,根據取模結果很快能夠定位到資料在哪個庫上

•資料量均衡,只要uid是均勻的,資料在各個庫上的分布一定是均衡的

•請求量均衡,只要uid是均勻的,負載在各個庫上的分布一定是均衡的

雜湊法的不足:

•擴容麻煩,如果容量不夠,要增加乙個庫,重新hash可能會導致資料遷移,如何平滑的進行資料遷移,是乙個需要解決的問題(這個以後有時間在進行說明)

二,使用者資料水平拆分後的問題

使用者資料通過水平拆分後,會帶來什麼問題呢?

1,對於uid的屬性查詢,可以直接用過uid取模路由找到儲存的庫,如:uid=12的資料,可以直接取模定位到資料在user-db0庫上面,對於非uid的資料查詢,比如現在要通過login_name來進行登陸,這個時候怎麼定位資料儲存的庫呢?(此問題雜湊法和範圍法同時存在)

根據樓主這些年的開發經驗,使用者中心業務需求通常有以下兩類:

使用者登入:通過login_name/phone/email查詢使用者的實體,1%請求屬於這種型別

使用者資訊查詢:登入之後,通過uid來查詢使用者的例項,99%請求屬這種型別

2,新增使用者時由於新使用者沒有uid,這個時候怎麼確定新使用者應該路由到哪個庫呢?

三,實現思路

對於非uid查詢使用者資訊的實現思路:

1,通過建立login_name與uid的對映關係來快速定位到相應的庫,這樣表的資料相對較少,可以儲存很多的資料,不足之處在於需要先查找到uid所屬的庫然後在查詢使用者資料,速度相對較慢

2,快取對映法,將login_name與uid儲存在記憶體中,如redis用login_name做key uid當做val,這樣只需要在redis中通過login_name查詢出uid然後取模即可對應到相應的庫,資料一旦儲存就不再需要增刪改,速度相對較快,不足之處在於需要多一次redis查詢並且比較消耗記憶體

3,uid通過一定的規則來生成,裡面融入login_name元素,下次通過login_name登入時只需要再次通過此規則來生成id然後取模就可以定位到資料庫了,不足之處在於演算法需要精心的設計並且有uid衝突的風險

對於新增使用者,新使用者沒有uid的資料路由實現思路

1,單獨的表,表進行id自增,寫乙個單獨的服務介面,每次新增使用者資料時先在這張表裡面新增一條資料然後返回最新的自增id,把id設定給uid,這樣就可以通過uid取模路由到相應的庫了(表的歷史自增資料可以進行定期清理)。不足之處在於每次都要訪問這張表,會有效能瓶頸。 

2,單獨的表,表裡面儲存最大的maxid,每次新增或者服務初始化的時候首先獲取表裡面的maxid,獲取maxid之後在maxid的基礎上加上1000-2000(具體多少按照資料量大小來定),然後把這些id通過迴圈寫入記憶體(比如redis),然後再把加上的數量+maxid重新寫入到maxid,下次沒有id或者快要用完的時候再拉取下一批id再次寫入(需要考慮重複寫入問題,比如分布式同一時刻用完一起拉取資料並寫入資料問題)。不足之處在於如果服務重啟了拉取的id如果沒有消耗完會導致id空洞問題,但是比第乙個方案效率更高

關於id有幾個注意的地方,如果你的id有可能出現在頁面上, 比如訂單id, 使用者id ,盡量不要使用自增的,因為別人可以根據這些id,看出你現在的使用者規模,訂單量。通過在兩天中相同時間下單,可以看出你的交易量,如果許可權沒有控制好,通過遍歷可以查出所有的資訊。 

我們這裡以使用者表為例,看把使用者資訊寫入資料庫的一些方法,以及這些方法的一些問題。 

四,總結

1,資料庫水平切分的兩種方案

範圍法雜湊法

2,資料庫水平切分後遇到的問題

用uid進行的操作可以定位到資料庫,但是通過login_name進行登入這種型別的需求無法直接定位到資料庫

3,通過非uid進行運算元據的解決方案

建立login_name與uid的對映關係表

將login_name與uid之前的對映關係存放在快取中,如redis

uid通過一定的規則來生成,裡面融入login_name元素

新增使用者,新使用者沒有uid的路由儲存實現方案

**: 

常用的資料庫表水平拆分方案

user uid,login name,passwd,age,nickname,其中uid為主鍵id,其它欄位為使用者屬性 此方案架構在業務初期單表單庫能夠搞定,但是隨著業務量的迅速增長,資料量越來越大時,這時候就需要對資料庫進行水平拆分了,常見的水平切分演算法有 範圍法 和 雜湊法 1,範圍發 以...

資料庫MySQL 資料庫表的水平拆分

表的水平拆分是為了解決單錶資料量過大的問題,水平拆分的表每乙個表的結構都是完全一致的,以下面的peyment表為例來說明 如果單錶的資料量達到上億條,那麼這時候我們儘管加了完美的索引,查詢效率低,寫入的效率也相應的降低。通常水平拆分的方法為 1 對customer id進行hash運算,如果要拆分為...

資料庫表如何水平拆分和垂直拆分

目前很多網際網路系統都存在單錶資料量過大的問題,這就降低了查詢速度,影響了客戶體驗。為了提高查詢速度,我們可以優化sql語句,優化表結構和索引,不過對那些百萬級千萬級的資料庫表,即便是優化過後,查詢速度還是滿足不了要求。這時候我們就可以通過分表降低單次查詢資料量,從而提高查詢速度,一般分表的方式有兩...