1. 程式人生 > >從使用者中心開始,聊“單KEY”類業務資料庫水平切分架構實踐

從使用者中心開始,聊“單KEY”類業務資料庫水平切分架構實踐

本文將以“使用者中心”為例,介紹“單KEY”類業務,隨著資料量的逐步增大,資料庫效能顯著降低,資料庫水平切分相關的架構實踐:

  • 如何來實施水平切分。

  • 水平切分後常見的問題 。

  • 典型問題的優化思路及實踐。

一、使用者中心

使用者中心是一個非常常見的業務,主要提供使用者註冊、登入、資訊查詢與修改的服務,其核心元資料為:

User(uid, login_name, passwd, sex, age, nickname, …)

其中:

  • uid為使用者ID,主鍵。

  • login_name, passwd, sex, age, nickname, …等使用者屬性。

資料庫設計上,一般來說在業務初期,單庫單表就能夠搞定這個需求,典型的架構設計為:

enter image description here

  • user-center:使用者中心服務,對呼叫者提供友好的RPC介面。

  • user-db:對使用者進行資料儲存。

二、使用者中心水平切分方法

當資料量越來越大時,需要多使用者中心進行水平切分,常見的水平切分演算法有“範圍法”和“雜湊法”。

範圍法

範圍法:以使用者中心的業務主鍵uid為劃分依據,將資料水平切分到兩個資料庫例項上去:

enter image description here

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

  • user-db2:儲存0到2千萬的uid資料。

範圍法的優點是:

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

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

範圍法的不足是:

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

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

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

雜湊法

雜湊法:也是以使用者中心的業務主鍵uid為劃分依據,將資料水平切分到兩個資料庫例項上去:

enter image description here

  • user-db1:儲存uid取模得1的uid資料。

  • user-db2:儲存uid取模得0的uid資料。

雜湊法的優點是:

  • 切分策略簡單,根據uid,按照hash,user-center很快能夠定位到資料在哪個庫上。

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

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

雜湊法的不足是:

  • 擴容麻煩,如果容量不夠,要增加一個庫,重新hash可能會導致資料遷移,如何平滑的進行資料遷移,是一個需要解決的問題

三、使用者中心水平切分後帶來的問題

使用uid來進行水平切分之後,整個使用者中心的業務訪問會遇到什麼問題呢?

對於uid屬性上的查詢可以直接路由到庫,假設訪問uid=124的資料,取模後能夠直接定位db-user1:

enter image description here

對於非uid屬性上的查詢,例如login_name屬性上的查詢,就悲劇了:

enter image description here

假設訪問login_name=shenjian的資料,由於不知道資料落在哪個庫上,往往需要遍歷所有庫,當分庫數量很多時,效能會顯著降低。

如何解決分庫後,非uid屬性上得查詢問題,是後文要重點討論的內容。

四、使用者中心非uid屬性查詢需求分析

任何脫離業務的架構設計都是耍流氓,在進行架構討論之前,先來對業務進行簡要分析,看非uid屬性上有哪些查詢需求。

根據樓主這些年的架構經驗,使用者中心非uid屬性上經常有兩類業務需求:

  • 使用者側,前臺訪問,最典型的有兩類需求。

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

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

    使用者側的查詢基本上是單條記錄的查詢,訪問量較大,服務需要高可用,並且對一致性的要求較高。

  • 運營側,後臺訪問,根據產品、運營需求,訪問模式各異。

    按照年齡、性別、頭像、登陸時間、註冊時間來進行查詢。

    運營側的查詢基本上是批量分頁的查詢,由於是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格。

這兩類不同的業務需求,應該使用什麼樣的架構方案來解決呢?

五、使用者中心水平切分架構思路

使用者中心在資料量較大的情況下,使用uid進行水平切分,對於非uid屬性上的查詢需求,架構設計的核心思路為:

  • 針對使用者側,應該採用“建立非uid屬性到uid的對映關係”的架構方案。

  • 針對運營側,應該採用“前臺與後臺分離”的架構方案。

六、使用者中心-使用者側最佳實踐

索引表法

思路:uid能直接定位到庫,loginname不能直接定位到庫,如果通過loginname能查詢到uid,問題解決。

解決方案

  • 建立一個索引表記錄login_name->uid的對映關係。

  • 用login_name來訪問時,先通過索引表查詢到uid,再定位相應的庫。

  • 索引表屬性較少,可以容納非常多資料,一般不需要分庫。

  • 如果資料量過大,可以通過login_name來分庫。

潛在不足:多一次資料庫查詢,效能下降一倍。

快取對映法

思路:訪問索引表效能較低,把對映關係放在快取裡效能更佳。

解決方案

  • login_name查詢先到cache中查詢uid,再根據uid定位資料庫。

  • 假設cache miss,掃描全庫獲取login_name對應的uid,放入cache.

  • login_name到uid的對映關係不會變化,對映關係一旦放入快取,不會更改,無需淘汰,快取命中率超高。

  • 如果資料量過大,可以通過login_name進行cache水平切分。

潛在不足:多一次cache查詢。

login_name生成uid

思路:不進行遠端查詢,由login_name直接得到uid.

解決方案

  • 在使用者註冊時,設計函式loginname生成uid,uid=f(loginname),按uid分庫插入資料。

  • 用loginname來訪問時,先通過函式計算出uid,即uid=f(loginname)再來一遍,由uid路由到對應庫。

潛在不足:該函式設計需要非常講究技巧,有uid生成衝突風險。

login_name基因融入uid

思路:不能用loginname生成uid,可以從loginname抽取“基因”,融入uid中:

enter image description here

假設分8庫,採用uid%8路由,潛臺詞是,uid的最後3個bit決定這條資料落在哪個庫上,這3個bit就是所謂的“基因”。

解決方案

  • 在使用者註冊時,設計函式loginname生成3bit基因,loginnamegene=f(loginname),如上圖粉色部分。

  • 同時,生成61bit的全域性唯一id,作為使用者的標識,如上圖綠色部分。

  • 接著把3bit的loginnamegene也作為uid的一部分,如上圖屎黃色部分。

  • 生成64bit的uid,由id和loginnamegene拼裝而成,並按照uid分庫插入資料。

  • 用loginname來訪問時,先通過函式由loginname再次復原3bit基因。

  • loginnamegene=f(loginname),通過loginname_gene%8直接定位到庫。

七、使用者中心-運營側最佳實踐

前臺使用者側,業務需求基本都是單行記錄的訪問,只要建立非uid屬性login_name/phone/email到uid的對映關係,就能解決問題。

後臺運營側,業務需求各異,基本是批量分頁的訪問,這類訪問計算量較大,返回資料量較大,比較消耗資料庫效能。

如果此時前臺業務和後臺業務公用一批服務和一個數據庫,有可能導致,由於後臺的“少數幾個請求”的“批量查詢”的“低效”訪問,導致資料庫的cpu偶爾瞬時100%,影響前臺正常使用者的訪問(例如,登入超時)。

enter image description here

而且,為了滿足後臺業務各類“奇形怪狀”的需求,往往會在資料庫上建立各種索引,這些索引佔用大量記憶體,會使得使用者側前臺業務uid/login_name上的查詢效能與寫入效能大幅度降低,處理時間增長。

對於這一類業務,應該採用“前臺與後臺分離”的架構方案:

enter image description here

使用者側前臺業務需求架構依然不變,產品運營側後臺業務需求則抽取獨立的web/service/db來支援,解除系統之間的耦合,對於“業務複雜”“併發量低”“無需高可用”“能接受一定延時”的後臺業務:

  • 可以去掉service層,在運營後臺web層通過dao直接訪問db.

  • 不需要反向代理,不需要叢集冗餘。

  • 不需要訪問實時庫,可以通過MQ或者線下非同步同步資料。

  • 在資料庫非常大的情況下,可以使用更契合大量資料允許接受更高延時的“索引外接”或者“HIVE”的設計方案。

enter image description here

八、總結

本文以“使用者中心”為典型的 “單KEY”類業務,水平切分的架構點,做了這樣一些介紹。

水平切分方式:

  • 範圍法

  • 雜湊法

水平切分後碰到的問題:

  • 通過uid屬性查詢能直接定位到庫,通過非uid屬性查詢不能定位到庫。

非uid屬性查詢的典型業務:

  • 使用者側,前臺訪問,單條記錄的查詢,訪問量較大,服務需要高可用,並且對一致性的要求較高。

  • 運營側,後臺訪問,根據產品、運營需求,訪問模式各異,基本上是批量分頁的查詢,由於是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格。

這兩類業務的架構設計思路:

  • 針對使用者側,應該採用“建立非uid屬性到uid的對映關係”的架構方案。

  • 針對運營側,應該採用“前臺與後臺分離”的架構方案。

使用者前臺側,“建立非uid屬性到uid的對映關係”最佳實踐:

  • 索引表法:資料庫中記錄login_name->uid的對映關係。

  • 快取對映法:快取中記錄login_name->uid的對映關係。

  • login_name生成uid.

  • login_name基因融入uid.

運營後臺側,“前臺與後臺分離”最佳實踐:

  • 前臺、後臺系統web/service/db分離解耦,避免後臺低效查詢引發前臺查詢抖動。

  • 可以採用資料冗餘的設計方式。

  • 可以採用“外接索引”(例如ES搜尋系統)或者“大資料處理”(例如HIVE)來滿足後臺變態的查詢需求。

九、還有哪些未盡事宜

  • 以帖子中心為典型的 “1對多”類業務。

  • 以好友關係為典型的“多對多”類業務。

  • 以訂單中心為典型的“多KEY”類業務。

他們的水平拆分架構又應該怎麼處理,敬請期待下期。

希望大家有收穫。