使用者中心,1億資料,架構如何設計?

架構師修行之路發表於2020-07-16

本文較長,可提前收藏。

使用者中心,幾乎是所有網際網路公司,必備的子系統。隨著資料量不斷增加,吞吐量不斷增大,使用者中心的架構,該如何演進呢。

什麼是使用者中心業務?
使用者中心是一個通用業務,主要提供使用者註冊、登入、資訊查詢與修改的服務

使用者中心的資料結構是怎麼樣的?
使用者中心的核心資料結構為:
User(uid, login_name, passwd, sex, age, nickname, …)

其中:
(1)uid為使用者ID,為主鍵;
(2)login_name, passwd, sex 等是使用者屬性;

其系統架構又是怎麼樣的呢?
在業務初期,單庫單表,配合使用者中心微服務,就能滿足絕大部分業務需求,其典型的架構為:
使用者中心,1億資料,架構如何設計?
(1)user-center:使用者中心服務,對呼叫者提供友好的RPC介面;
(2)user-db:對使用者進行資料儲存;

當資料量越來越大,例如達到1億註冊量時,會出現什麼問題呢?
隨著資料量越來越大,單庫無法承載所有的資料,此時需要對資料庫進行水平切分。

常見的水平切分演算法有“範圍法”和“雜湊法”。

水平切分,什麼是範圍法?
範圍法,以使用者中心的業務主鍵uid為劃分依據,採用區間的方式,將資料水平切分到兩個資料庫例項上去:
使用者中心,1億資料,架構如何設計?
(1)user-db1:儲存0到1千萬的uid資料;
(2)user-db2:儲存1千萬到2千萬的uid資料;

範圍法有什麼優點?
(1)切分策略簡單,根據uid,按照範圍,user-center很快能夠定位到資料在哪個庫上;
(2)擴容簡單,如果容量不夠,只要增加user-db3,擴充2千萬到3千萬的uid即可;

範圍法有什麼缺點?
(1)uid必須要滿足遞增的特性
(2)資料量不均,新增的user-db3,在初期的資料會比較少;
(3)請求量不均,一般來說,新註冊的使用者活躍度會比較高,故user-db2往往會比user-db1負載要高,導致伺服器利用率不平衡;
畫外音:資料庫層面的負載均衡,既要考慮資料量的均衡,又要考慮負載的均衡。

水平切分,什麼是雜湊法?
雜湊法,也是以使用者中心的業務主鍵uid為劃分依據,採用雜湊的方式將資料水平切分到兩個資料庫例項上去:
使用者中心,1億資料,架構如何設計?
(1)user-db1:儲存奇數的uid資料;
(2)user-db2:儲存偶數的uid資料;

雜湊法有什麼優點?
(1)切分策略簡單,根據uid,按照hash,user-center很快能夠定位到資料在哪個庫上;
(2)資料量均衡,只要uid是隨機的,資料在各個庫上的分佈一定是均衡的;
(3)請求量均衡,只要uid隨機的,負載在各個庫上的分佈一定是均衡的;
畫外音:如果採用分散式id生成器,id的生成,一般都是隨機的。

雜湊法有什麼缺點?
(1)擴容麻煩,如果容量不夠,要增加一個庫,重新hash可能會導致資料遷移;

使用者中心架構,實施了水平切分之後,會帶來什麼新的問題呢?
使用uid來進行水平切分之後,對於uid屬性上的查詢,可以直接路由到庫,假設訪問uid=124的資料,取模後能夠直接定位db-user1
使用者中心,1億資料,架構如何設計?

對於非uid屬性上的查詢,就悲劇了,例如login_name屬性上的查詢
使用者中心,1億資料,架構如何設計?
假設訪問login_name=shenjian的資料,由於不知道資料落在哪個庫上,往往需要遍歷所有庫,當分庫數量多起來,效能會顯著降低。

使用者中心,非uid屬性查詢,有哪些業務場景?

任何脫離業務的架構設計都是耍流氓。

在進行架構討論之前,先來對業務進行簡要分析,使用者中心非uid屬性上,有兩類典型的業務需求

第一大類,使用者側,前臺訪問,最典型的有兩類需求:
(1)使用者登入:透過登入名login_name查詢使用者的實體,1%請求屬於這種型別;
(2)使用者資訊查詢:登入之後,透過uid來查詢使用者的例項,99%請求屬這種型別;

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

第二大類,運營側,後臺訪問,根據產品、運營需求,訪問模式各異,按照年齡、性別、頭像、登陸時間、註冊時間來進行查詢。

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

對於這兩類不同的業務需求,應該使用什麼樣的架構方案來解決呢?
總的來說,針對這兩類業務需求,架構設計的核心思路為:
(1)使用者側,採用“建立非uid屬性到uid的對映關係”的架構方案;
(2)運營側,採用“前臺與後臺分離”的架構方案;

使用者側,如何實施“建立非uid屬性到uid的對映關係”呢?
常見的方法有四種
(1)索引表法
(2)快取對映法
(3)生成uid
(4)基因法
接下來,我們們一一介紹。

什麼是,索引表法?
索引表法的思路是:uid能直接定位到庫,login_name不能直接定位到庫,如果透過login_name能查詢到uid,問題便能得到解決。

具體的解決方案如下:
(1)建立一個索引表記錄login_nameuid的對映關係;
(2)用login_name來訪問時,先透過索引表查詢到uid,再透過uid定位相應的庫
(3)索引表屬性較少,可以容納非常多資料,一般不需要分庫;
(4)如果資料量過大,可以透過login_name來分庫;

索引表法,有什麼缺點呢?
資料訪問,會增加一次資料庫查詢,效能會有所下降。

什麼是,快取對映法?
快取對映法的思路是:訪問索引表效能較低,把對映關係放在快取裡,能夠提升效能。

具體的決方案如下:
(1)login_name查詢先到cache中查詢uid,再根據uid定位資料庫;
(2)假設cache miss,掃描所有分庫,獲取login_name對應的uid,放入cache
(3)login_nameuid的對映關係不會變化,對映關係一旦放入快取,不會更改,無需淘汰,快取命中率超高
(4)如果資料量過大,可以透過login_name進行cache水平切分;

快取對映法,有什麼缺點呢?
仍然多了一次網路互動,即一次cache查詢。

什麼是,生成uid法?
生成uid法的思路是:不進行遠端查詢,由login_name直接得到uid

具體的決方案如下:
(1)在使用者註冊時,設計函式login_name生成uiduid=f(login_name),按uid分庫插入資料;
(2)用login_name來訪問時,先透過函式計算出uid,即uid=f(login_name)再來一遍,由uid路由到對應庫;

生成uid法,有什麼缺點呢?
該函式設計需要非常講究技巧,且uid生成衝突風險
畫外音:uid衝突,是業務無法接受的,故生產環境中,一般不使用這個方法。

什麼是,基因法?
基因法的思路是:不能用login_name生成uid,但可以login_name抽取“基因”,融入uid

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

具體的決方案如下:
使用者中心,1億資料,架構如何設計?
(1)在使用者註冊時,設計函式login_name生成3bit基因,login_name_gene = f(login_name),如上圖粉色部分;
(2)同時,生成61bit的全域性唯一id,作為使用者的標識,如上圖綠色部分;
(3)接著把3bit的login_name_gene也作為uid的一部分,如上圖屎黃色部分;
(4)生成64bit的uid,由id和login_name_gene拼裝而成,並按照uid分庫插入資料;
(5)用login_name來訪問時,先透過函式由login_name再次復原3bit基因,login_name_gene = f(login_name),透過login_name_gene%8直接定位到庫;
畫外音:基因法,有點意思,在分庫時經常使用。

使用者側,如何實施“前臺與後臺分離”的架構方案呢?
前臺使用者側,業務需求基本都是單行記錄的訪問,只要建立非uid屬性login_nameuid的對映關係,就能解決問題。

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

此時的架構,存在什麼問題?
此時,前臺業務和後臺業務共用一批服務和一個資料庫,有可能導致,由於後臺的“少數幾個請求”的“批次查詢”的“低效”訪問,導致資料庫的cpu偶爾瞬時100%,影響前臺正常使用者的訪問(例如,登入超時)。
畫外音:本質上,是系統的耦合。
使用者中心,1億資料,架構如何設計?
而且,為了滿足後臺業務各類“奇形怪狀”的需求,往往會在資料庫上建立各種索引,這些索引佔用大量記憶體,會使得使用者側前臺業務uid/login_name上的查詢效能與寫入效能大幅度降低,處理時間增長。

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

什麼是,前臺與後臺分離的架構方案?
使用者中心,1億資料,架構如何設計?
使用者側前臺業務需求架構依然不變,產品運營側後臺業務需求則抽取獨立的 web / service / db 來支援,解除系統之間的耦合,對於“業務複雜”“併發量低”“無需高可用”“能接受一定延時”的後臺業務:
(1)可以去掉service層,在運營後臺web層透過dao直接訪問db
(2)不需要反向代理,不需要叢集冗餘;
(3)不需要訪問實時庫,可以透過MQ或者線下非同步同步資料;
(4)在資料庫非常大的情況下,可以使用更契合大量資料允許接受更高延時的“索引外接”或者“HIVE”的設計方案;
使用者中心,1億資料,架構如何設計?

總結
使用者中心,是典型的“單KEY”類業務,這一類業務,都可以使用上述架構方案

常見的資料庫水平切分方式有兩種
(1)範圍法
(2)雜湊法

水平切分後碰到的問題是:
(1)透過uid屬性查詢能直接定位到庫,透過非uid屬性查詢不能定位到庫

uid屬性查詢,有兩類典型的業務
(1)使用者側,前臺訪問,單條記錄的查詢,訪問量較大,服務需要高可用,並且對一致性的要求較高;
(2)運營側,後臺訪問,根據產品、運營需求,訪問模式各異,基本上是批次分頁的查詢,由於是內部系統,訪問量很低,對可用性的要求不高,對一致性的要求也沒這麼嚴格;

針對這兩類業務,架構設計的思路是
(1)使用者側,採用“建立非uid屬性到uid的對映關係”的架構方案;
(2)運營側,採用“前臺與後臺分離”的架構方案;

前臺使用者側,“建立非uid屬性到uid的對映關係”,有四種常見的實踐
(1)索引表法:資料庫中記錄login_nameuid的對映關係;
(2)快取對映法:快取中記錄login_nameuid的對映關係;
(3)生成uidlogin_name生成uid
(4)基因法login_name基因融入uid

後臺運營側,“前臺與後臺分離”的最佳實踐是
(1)前臺、後臺系統 web/service/db 分離解耦,避免後臺低效查詢引發前臺查詢抖動;
(2)可以採用資料冗餘的設計方式;
(3)可以採用“外接索引”(例如ES搜尋系統)或者“大資料處理”(例如HIVE)來滿足後臺變態的查詢需求;

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69959420/viewspace-2704837/,如需轉載,請註明出處,否則將追究法律責任。

相關文章