本文將以“用戶中心”為例,介紹“單KEY”類業(yè)務(wù)葫督,隨著數(shù)據(jù)量的逐步增大竭鞍,數(shù)據(jù)庫性能顯著降低,數(shù)據(jù)庫水平切分相關(guān)的架構(gòu)實(shí)踐:
如何來實(shí)施水平切分
水平切分后常見的問題
典型問題的優(yōu)化思路及實(shí)踐
一橄镜、用戶中心
用戶中心是一個(gè)非常常見的業(yè)務(wù)偎快,主要提供用戶注冊、登錄洽胶、信息查詢與修改的服務(wù)晒夹,其核心元數(shù)據(jù)為:
User(uid, login_name, passwd, sex, age, nickname, …)
其中:
uid為用戶ID,主鍵
login_name, passwd, sex, age, nickname, …等用戶屬性
數(shù)據(jù)庫設(shè)計(jì)上,一般來說在業(yè)務(wù)初期丐怯,單庫單表就能夠搞定這個(gè)需求喷好,典型的架構(gòu)設(shè)計(jì)為:
user-center:用戶中心服務(wù),對調(diào)用者提供友好的RPC接口
user-db:對用戶進(jìn)行數(shù)據(jù)存儲(chǔ)
二读跷、用戶中心水平切分方法
當(dāng)數(shù)據(jù)量越來越大時(shí)梗搅,需要對數(shù)據(jù)庫進(jìn)行水平切分,常見的水平切分算法有“范圍法”和“哈希法”效览。
范圍法无切,以用戶中心的業(yè)務(wù)主鍵uid為劃分依據(jù),將數(shù)據(jù)水平切分到兩個(gè)數(shù)據(jù)庫實(shí)例上去:
user-db1:存儲(chǔ)0到1千萬的uid數(shù)據(jù)
user-db2:存儲(chǔ)1到2千萬的uid數(shù)據(jù)
范圍法的優(yōu)點(diǎn)是:
切分策略簡單钦铺,根據(jù)uid开财,按照范圍禀酱,user- center很快能夠定位到數(shù)據(jù)在哪個(gè)庫上
擴(kuò)容簡單酸茴,如果容量不夠,只要增加user-db3即可
范圍法的不足是:
uid必須要滿足遞增的特性
數(shù)據(jù)量不均沼本,新增的user-db3噩峦,在初期的數(shù)據(jù)會(huì)比較少
請求量不均,一般來說抽兆,新注冊的用戶活躍度會(huì)比較高识补,故user-db2往往會(huì)比user-db1負(fù)載要高,導(dǎo)致服務(wù)器利用率不平衡
哈希法辫红,也是以用戶中心的業(yè)務(wù)主鍵uid為劃分依據(jù)凭涂,將數(shù)據(jù)水平切分到兩個(gè)數(shù)據(jù)庫實(shí)例上去:
user-db1:存儲(chǔ)uid取模得1的uid數(shù)據(jù)
user-db2:存儲(chǔ)uid取模得0的uid數(shù)據(jù)
哈希法的優(yōu)點(diǎn)是:
切分策略簡單,根據(jù)uid贴妻,按照hash切油,user-center很快能夠定位到數(shù)據(jù)在哪個(gè)庫上
數(shù)據(jù)量均衡,只要uid是均勻的名惩,數(shù)據(jù)在各個(gè)庫上的分布一定是均衡的
請求量均衡澎胡,只要uid是均勻的,負(fù)載在各個(gè)庫上的分布一定是均衡的
哈希法的不足是:
擴(kuò)容麻煩娩鹉,如果容量不夠攻谁,要增加一個(gè)庫,重新hash可能會(huì)導(dǎo)致數(shù)據(jù)遷移弯予,如何平滑的進(jìn)行數(shù)據(jù)遷移戚宦,是一個(gè)需要解決的問題
三、用戶中心水平切分后帶來的問題
使用uid來進(jìn)行水平切分之后锈嫩,整個(gè)用戶中心的業(yè)務(wù)訪問會(huì)遇到什么問題呢阁苞?
對于uid屬性上的查詢可以直接路由到庫困檩,假設(shè)訪問uid=124的數(shù)據(jù)祠挫,取模后能夠直接定位db-user1:
對于非uid屬性上的查詢那槽,例如login_name屬性上的查詢,就悲劇了:
假設(shè)訪問login_name=shenjian的數(shù)據(jù)等舔,由于不知道數(shù)據(jù)落在哪個(gè)庫上骚灸,往往需要遍歷所有庫,當(dāng)分庫數(shù)量多起來慌植,性能會(huì)顯著降低甚牲。
如何解決分庫后,非uid屬性上的查詢問題蝶柿,是后文要重點(diǎn)討論的內(nèi)容丈钙。
四、用戶中心非uid屬性查詢需求分析
任何脫離業(yè)務(wù)的架構(gòu)設(shè)計(jì)都是耍流氓交汤,在進(jìn)行架構(gòu)討論之前雏赦,先來對業(yè)務(wù)進(jìn)行簡要分析,看非uid屬性上有哪些查詢需求芙扎。
根據(jù)樓主這些年的架構(gòu)經(jīng)驗(yàn)星岗,用戶中心非uid屬性上經(jīng)常有兩類業(yè)務(wù)需求:
(1)用戶側(cè),前臺(tái)訪問戒洼,最典型的有兩類需求
用戶登錄:通過login_name/phone/email查詢用戶的實(shí)體俏橘,1%請求屬于這種類型
用戶信息查詢:登錄之后,通過uid來查詢用戶的實(shí)例圈浇,99%請求屬這種類型
用戶側(cè)的查詢基本上是單條記錄的查詢寥掐,訪問量較大,服務(wù)需要高可用磷蜀,并且對一致性的要求較高召耘。
(2)運(yùn)營側(cè),后臺(tái)訪問蠕搜,根據(jù)產(chǎn)品怎茫、運(yùn)營需求,訪問模式各異妓灌,按照年齡轨蛤、性別、頭像虫埂、登陸時(shí)間祥山、注冊時(shí)間來進(jìn)行查詢。
運(yùn)營側(cè)的查詢基本上是批量分頁的查詢掉伏,由于是內(nèi)部系統(tǒng)缝呕,訪問量很低澳窑,對可用性的要求不高,對一致性的要求也沒這么嚴(yán)格供常。
這兩類不同的業(yè)務(wù)需求摊聋,應(yīng)該使用什么樣的架構(gòu)方案來解決呢?
五栈暇、用戶中心水平切分架構(gòu)思路
用戶中心在數(shù)據(jù)量較大的情況下麻裁,使用uid進(jìn)行水平切分,對于非uid屬性上的查詢需求源祈,架構(gòu)設(shè)計(jì)的核心思路為:
針對用戶側(cè)煎源,應(yīng)該采用“建立非uid屬性到uid的映射關(guān)系”的架構(gòu)方案
針對運(yùn)營側(cè),應(yīng)該采用“前臺(tái)與后臺(tái)分離”的架構(gòu)方案
六香缺、用戶中心-用戶側(cè)最佳實(shí)踐
【索引表法】
思路:uid能直接定位到庫手销,login_name不能直接定位到庫,如果通過login_name能查詢到uid图张,問題解決
解決方案:
建立一個(gè)索引表記錄login_name->uid的映射關(guān)系
用login_name來訪問時(shí)锋拖,先通過索引表查詢到uid,再定位相應(yīng)的庫
索引表屬性較少埂淮,可以容納非常多數(shù)據(jù)姑隅,一般不需要分庫
如果數(shù)據(jù)量過大,可以通過login_name來分庫
潛在不足:多一次數(shù)據(jù)庫查詢倔撞,性能下降一倍
【緩存映射法】
思路:訪問索引表性能較低讲仰,把映射關(guān)系放在緩存里性能更佳
解決方案:
login_name查詢先到cache中查詢uid,再根據(jù)uid定位數(shù)據(jù)庫
假設(shè)cache miss痪蝇,采用掃全庫法獲取login_name對應(yīng)的uid鄙陡,放入cache
login_name到uid的映射關(guān)系不會(huì)變化,映射關(guān)系一旦放入緩存躏啰,不會(huì)更改趁矾,無需淘汰,緩存命中率超高
如果數(shù)據(jù)量過大给僵,可以通過login_name進(jìn)行cache水平切分
潛在不足:多一次cache查詢
【login_name生成uid】
思路:不進(jìn)行遠(yuǎn)程查詢毫捣,由login_name直接得到uid
解決方案:
在用戶注冊時(shí),設(shè)計(jì)函數(shù)login_name生成uid帝际,uid=f(login_name)蔓同,按uid分庫插入數(shù)據(jù)
用login_name來訪問時(shí),先通過函數(shù)計(jì)算出uid蹲诀,即uid=f(login_name)再來一遍斑粱,由uid路由到對應(yīng)庫
潛在不足:該函數(shù)設(shè)計(jì)需要非常講究技巧,有uid生成沖突風(fēng)險(xiǎn)
【login_name基因融入uid】
思路:不能用login_name生成uid脯爪,可以從login_name抽取“基因”则北,融入uid中
假設(shè)分8庫矿微,采用uid%8路由,潛臺(tái)詞是尚揣,uid的最后3個(gè)bit決定這條數(shù)據(jù)落在哪個(gè)庫上涌矢,這3個(gè)bit就是所謂的“基因”。
解決方案:
在用戶注冊時(shí)惑艇,設(shè)計(jì)函數(shù)login_name生成3bit基因蒿辙,login_name_gene=f(login_name),如上圖粉色部分
同時(shí)滨巴,生成61bit的全局唯一id,作為用戶的標(biāo)識俺叭,如上圖綠色部分
接著把3bit的login_name_gene也作為uid的一部分恭取,如上圖屎黃色部分
生成64bit的uid,由id和login_name_gene拼裝而成熄守,并按照uid分庫插入數(shù)據(jù)
用login_name來訪問時(shí)蜈垮,先通過函數(shù)由login_name再次復(fù)原3bit基因,login_name_gene=f(login_name)裕照,通過login_name_gene%8直接定位到庫
七攒发、用戶中心-運(yùn)營側(cè)最佳實(shí)踐
前臺(tái)用戶側(cè),業(yè)務(wù)需求基本都是單行記錄的訪問晋南,只要建立非uid屬性?login_name / phone / email到uid的映射關(guān)系惠猿,就能解決問題。
后臺(tái)運(yùn)營側(cè)负间,業(yè)務(wù)需求各異偶妖,基本是批量分頁的訪問,這類訪問計(jì)算量較大政溃,返回?cái)?shù)據(jù)量較大趾访,比較消耗數(shù)據(jù)庫性能。
如果此時(shí)前臺(tái)業(yè)務(wù)和后臺(tái)業(yè)務(wù)公用一批服務(wù)和一個(gè)數(shù)據(jù)庫董虱,有可能導(dǎo)致扼鞋,由于后臺(tái)的“少數(shù)幾個(gè)請求”的“批量查詢”的“低效”訪問,導(dǎo)致數(shù)據(jù)庫的cpu偶爾瞬時(shí)100%愤诱,影響前臺(tái)正常用戶的訪問(例如云头,登錄超時(shí))。
而且转锈,為了滿足后臺(tái)業(yè)務(wù)各類“奇形怪狀”的需求盘寡,往往會(huì)在數(shù)據(jù)庫上建立各種索引,這些索引占用大量內(nèi)存撮慨,會(huì)使得用戶側(cè)前臺(tái)業(yè)務(wù)uid/login_name上的查詢性能與寫入性能大幅度降低竿痰,處理時(shí)間增長脆粥。
對于這一類業(yè)務(wù),應(yīng)該采用“前臺(tái)與后臺(tái)分離”的架構(gòu)方案:
用戶側(cè)前臺(tái)業(yè)務(wù)需求架構(gòu)依然不變影涉,產(chǎn)品運(yùn)營側(cè)后臺(tái)業(yè)務(wù)需求則抽取獨(dú)立的web / service / db來支持变隔,解除系統(tǒng)之間的耦合,對于“業(yè)務(wù)復(fù)雜”“并發(fā)量低”“無需高可用”“能接受一定延時(shí)”的后臺(tái)業(yè)務(wù):
可以去掉service層蟹倾,在運(yùn)營后臺(tái)web層通過dao直接訪問db
不需要反向代理匣缘,不需要集群冗余
不需要訪問實(shí)時(shí)庫,可以通過MQ或者線下異步同步數(shù)據(jù)
在數(shù)據(jù)庫非常大的情況下鲜棠,可以使用更契合大量數(shù)據(jù)允許接受更高延時(shí)的“索引外置”或者“HIVE”的設(shè)計(jì)方案
八肌厨、總結(jié)
將以“用戶中心”為典型的“單KEY”類業(yè)務(wù),水平切分的架構(gòu)點(diǎn)豁陆,本文做了這樣一些介紹柑爸。
水平切分方式:
范圍法
哈希法
水平切分后碰到的問題:
通過uid屬性查詢能直接定位到庫,通過非uid屬性查詢不能定位到庫
非uid屬性查詢的典型業(yè)務(wù):
用戶側(cè)盒音,前臺(tái)訪問表鳍,單條記錄的查詢,訪問量較大祥诽,服務(wù)需要高可用譬圣,并且對一致性的要求較高
運(yùn)營側(cè),后臺(tái)訪問雄坪,根據(jù)產(chǎn)品厘熟、運(yùn)營需求,訪問模式各異诸衔,基本上是批量分頁的查詢盯漂,由于是內(nèi)部系統(tǒng),訪問量很低笨农,對可用性的要求不高就缆,對一致性的要求也沒這么嚴(yán)格
這兩類業(yè)務(wù)的架構(gòu)設(shè)計(jì)思路:
針對用戶側(cè),應(yīng)該采用“建立非uid屬性到uid的映射關(guān)系”的架構(gòu)方案
針對運(yùn)營側(cè)谒亦,應(yīng)該采用“前臺(tái)與后臺(tái)分離”的架構(gòu)方案
用戶前臺(tái)側(cè)竭宰,“建立非uid屬性到uid的映射關(guān)系”最佳實(shí)踐:
索引表法:數(shù)據(jù)庫中記錄login_name->uid的映射關(guān)系
緩存映射法:緩存中記錄login_name->uid的映射關(guān)系
login_name生成uid
login_name基因融入uid
運(yùn)營后臺(tái)側(cè),“前臺(tái)與后臺(tái)分離”最佳實(shí)踐:
前臺(tái)份招、后臺(tái)系統(tǒng)web/service/db分離解耦切揭,避免后臺(tái)低效查詢引發(fā)前臺(tái)查詢抖動(dòng)
可以采用數(shù)據(jù)冗余的設(shè)計(jì)方式
可以采用“外置索引”(例如ES搜索系統(tǒng))或者“大數(shù)據(jù)處理”(例如HIVE)來滿足后臺(tái)變態(tài)的查詢需求
轉(zhuǎn)自:沈劍的微信文章