在Feed系統(tǒng)中,有簡(jiǎn)單數(shù)據(jù)類型的緩存,有集合類數(shù)據(jù)的。還有一些個(gè)性業(yè)務(wù)的緩存。比如大量的計(jì)數(shù)器場(chǎng)景,存在性判斷場(chǎng)景等。微博解決存在性判斷業(yè)務(wù)的緩存層叫EXISTENCE 緩存層,解決計(jì)算器場(chǎng)景的緩存叫COUNTER緩存。 EXISTENCE 緩存層主要用于緩存各種存在性判斷的業(yè)務(wù),諸如是否已贊(liked)、是否已閱讀(readed)這類需求。 Feed系統(tǒng)內(nèi)部有大量的計(jì)數(shù)場(chǎng)景,如用戶維度有關(guān)注數(shù)、粉絲數(shù)、feed發(fā)表數(shù),feed維度有轉(zhuǎn)發(fā)數(shù)、評(píng)論數(shù)、贊數(shù)以及閱讀數(shù)等。前面提到,按照傳統(tǒng)Redis、Memcached計(jì)數(shù)緩存方案,單單存每日新增的十億級(jí)的計(jì)數(shù),就需要新占用百G級(jí)的內(nèi)存,成本開(kāi)銷巨大。因此微博開(kāi)發(fā)了計(jì)數(shù)服務(wù)組件CounterService。下面以計(jì)數(shù)場(chǎng)景來(lái)管中窺豹。 提出問(wèn)題 對(duì)于計(jì)數(shù)業(yè)務(wù),經(jīng)典的構(gòu)建模型有兩種:1 db+cache模式,全量計(jì)數(shù)存在db,熱數(shù)據(jù)通過(guò)cache加速;2全量存在Redis中。方案1 通用成熟,但對(duì)于一致性要求較高的計(jì)數(shù)服務(wù),以及在海量數(shù)據(jù)和高并發(fā)訪問(wèn)場(chǎng)景下,支持不夠友好,運(yùn)維成本和硬件成本較高,微博上線初期曾使用該方案,在Redis面世后很快用新方案代替。方案2基于Redis的計(jì)數(shù)接口INCR、DECR,能很方便的實(shí)現(xiàn)通用的計(jì)數(shù)緩存模型,再通過(guò)hash分表,master-slave部署方式,可以實(shí)現(xiàn)一個(gè)中小規(guī)模的計(jì)數(shù)服務(wù)。 但在面對(duì)千億級(jí)的歷史海量計(jì)數(shù)以及每天十億級(jí)的新增計(jì)數(shù),直接使用Redis的計(jì)數(shù)模型存在嚴(yán)重的成本和性能問(wèn)題。首先Redis計(jì)數(shù)作為通用的全內(nèi)存計(jì)數(shù)模型,內(nèi)存效率不高。存儲(chǔ)一個(gè)key為8字節(jié)(long型id)、value為4字節(jié)的計(jì)數(shù),Redis至少需要耗費(fèi)65字節(jié)。1000億計(jì)數(shù)需要100G*65=6.5T以上的內(nèi)存,算上一個(gè)master配3個(gè)slave的開(kāi)銷,總共需要26T以上的內(nèi)存,按單機(jī)內(nèi)存96G計(jì)算,扣掉Redis其他內(nèi)存管理開(kāi)銷、系統(tǒng)占用,需要300-400臺(tái)機(jī)器。如果算上多機(jī)房,需要的機(jī)器數(shù)會(huì)更多。其次Redis計(jì)數(shù)模型的獲取性能不高。一條微博至少需要3個(gè)計(jì)數(shù)查詢,單次feed請(qǐng)求如果包含15條微博,僅僅微博計(jì)數(shù)就需要45個(gè)計(jì)數(shù)查詢。 解決問(wèn)題 在Feed系統(tǒng)的計(jì)數(shù)場(chǎng)景,單條feed的各種計(jì)數(shù)都有相同的key(即微博id),可以把這些計(jì)數(shù)存儲(chǔ)在一起,就能節(jié)省大量的key的存儲(chǔ)空間,讓1000億計(jì)數(shù)變成了330億條記錄;近一半的微博沒(méi)有轉(zhuǎn)、評(píng)論、贊,拋棄db+cache的方案,改用全量存儲(chǔ)的方案,對(duì)于沒(méi)有計(jì)數(shù)為0的微博不再存儲(chǔ),如果查不到就返回0,這樣330億條記錄只需要存160億條記錄。然后又對(duì)存儲(chǔ)結(jié)構(gòu)做了進(jìn)一步優(yōu)化,三個(gè)計(jì)數(shù)和key一起一共只需要8+4*3=20字節(jié)??偣仓恍枰?6G*20=320G,算上1主3從,總共也就只需要1.28T,只需要15臺(tái)左右機(jī)器即可。同時(shí)進(jìn)一步通過(guò)對(duì)CounterService增加SSD擴(kuò)展支持,按table滾動(dòng),老數(shù)據(jù)落在ssd,新數(shù)據(jù)、熱數(shù)據(jù)在內(nèi)存,1.28T的容量幾乎可以用單臺(tái)機(jī)器來(lái)承載(當(dāng)然考慮訪問(wèn)性能、可用性,還是需要hash到多個(gè)緩存節(jié)點(diǎn),并添加主從結(jié)構(gòu))。 計(jì)數(shù)器組件的架構(gòu)如圖13-14,主要特性如下: 1) 內(nèi)存優(yōu)化:通過(guò)預(yù)先分配的內(nèi)存數(shù)組Table存儲(chǔ)計(jì)數(shù),并且采用 double hash 解決沖突,避免Redis 實(shí)現(xiàn)中的大量指針開(kāi)銷。 2) Schema支持多列:一個(gè)feed id對(duì)應(yīng)的多個(gè)計(jì)數(shù)可以作為一條計(jì)數(shù)記錄,還支持動(dòng)態(tài)增減計(jì)數(shù)列,每列的計(jì)數(shù)內(nèi)存使用精簡(jiǎn)到bit; 3) 冷熱數(shù)據(jù)分離,根據(jù)時(shí)間維度,近期的熱數(shù)據(jù)放在內(nèi)存,之前的冷數(shù)據(jù)放在磁盤,降低機(jī)器成本; 4) LRU緩存:之前的冷數(shù)據(jù)如果被頻繁訪問(wèn)則放到LRU緩存進(jìn)行加速; 5) 異步IO線程訪問(wèn)冷數(shù)據(jù):冷數(shù)據(jù)的加載不影響服務(wù)的整體性能。
通過(guò)上述的擴(kuò)展,內(nèi)存占用降為之前的5-10%以下,同時(shí)一條feed的評(píng)論/贊等多個(gè)計(jì)數(shù)、一個(gè)用戶的粉絲/關(guān)注/微博等多個(gè)計(jì)數(shù)都可以一次性獲取,讀取性能大幅提升,基本徹底解決了計(jì)數(shù)業(yè)務(wù)的成本及性能問(wèn)題。 |
|
來(lái)自: xujin3 > 《特定系統(tǒng)》