歡迎光臨
每天分享高質量文章

每日數十億級業務下的計數器如何擴展Redis?

在Feed系統中,有簡單資料型別的快取,有集合類資料的。還有一些個性業務的快取。比如大量的計數器場景,存在性判斷場景等。微博解決存在性判斷業務的快取層叫EXISTENCE 快取層,解決計算器場景的快取叫COUNTER快取。

EXISTENCE 快取層主要用於快取各種存在性判斷的業務,諸如是否已贊(liked)、是否已閱讀(readed)這類需求。

Feed系統內部有大量的計數場景,如用戶維度有關註數、粉絲數、feed發表數,feed維度有轉發數、評論數、贊數以及閱讀數等。前面提到,按照傳統Redis、Memcached計數快取方案,單單存每日新增的十億級的計數,就需要新占用百G級的記憶體,成本開銷巨大。因此微博開發了計數服務組件CounterService。下麵以計數場景來管中窺豹。

提出問題

對於計數業務,經典的構建模型有兩種:1 db+cache樣式,全量計數存在db,熱資料通過cache加速;2全量存在Redis中。方案1 通用成熟,但對於一致性要求較高的計數服務,以及在海量資料和高併發訪問場景下,支持不夠友好,運維成本和硬體成本較高,微博上線初期曾使用該方案,在Redis面世後很快用新方案代替。方案2基於Redis的計數接口INCR、DECR,能很方便的實現通用的計數快取模型,再通過hash分表,master-slave部署方式,可以實現一個中小規模的計數服務。

但在面對千億級的歷史海量計數以及每天十億級的新增計數,直接使用Redis的計數模型存在嚴重的成本和性能問題。首先Redis計數作為通用的全記憶體計數模型,記憶體效率不高。儲存一個key為8位元組(long型id)、value為4位元組的計數,Redis至少需要耗費65位元組。1000億計數需要100G*65=6.5T以上的記憶體,算上一個master配3個slave的開銷,總共需要26T以上的記憶體,按單機記憶體96G計算,扣掉Redis其他記憶體管理開銷、系統占用,需要300-400台機器。如果算上多機房,需要的機器數會更多。其次Redis計數模型的獲取性能不高。一條微博至少需要3個計數查詢,單次feed請求如果包含15條微博,僅僅微博計數就需要45個計數查詢。

解決問題

在Feed系統的計數場景,單條feed的各種計數都有相同的key(即微博id),可以把這些計數儲存在一起,就能節省大量的key的儲存空間,讓1000億計數變成了330億條記錄;近一半的微博沒有轉、評論、贊,拋棄db+cache的方案,改用全量儲存的方案,對於沒有計數為0的微博不再儲存,如果查不到就傳回0,這樣330億條記錄只需要存160億條記錄。然後又對儲存結構做了進一步優化,三個計數和key一起一共只需要8+4*3=20位元組。總共只需要16G*20=320G,算上1主3從,總共也就只需要1.28T,只需要15台左右機器即可。同時進一步通過對CounterService增加SSD擴展支持,按table滾動,老資料落在ssd,新資料、熱資料在記憶體,1.28T的容量幾乎可以用單台機器來承載(當然考慮訪問性能、可用性,還是需要hash到多個快取節點,並添加主從結構)。

計數器組件的架構如圖13-14,主要特性如下:

1)  記憶體優化:通過預先分配的記憶體陣列Table儲存計數,並且採用 double hash 解決衝突,避免Redis 實現中的大量指標開銷。

2)  Schema支持多列:一個feed id對應的多個計數可以作為一條計數記錄,還支持動態增減計數列,每列的計數記憶體使用精簡到bit;

3)  冷熱資料分離,根據時間維度,近期的熱資料放在記憶體,之前的冷資料放在磁盤,降低機器成本;

4)  LRU快取:之前的冷資料如果被頻繁訪問則放到LRU快取進行加速;

5)  異步IO執行緒訪問冷資料:冷資料的加載不影響服務的整體性能。

圖 13-14 基於Redis擴展後的計數器儲存架構

通過上述的擴展,記憶體占用降為之前的5-10%以下,同時一條feed的評論/贊等多個計數、一個用戶的粉絲/關註/微博等多個計數都可以一次性獲取,讀取性能大幅提升,基本徹底解決了計數業務的成本及性能問題。

欲瞭解更多有關分佈式快取方面的內容,請閱讀《深入分佈式快取:從原理到實踐》一書。

 

京東購書,掃描二維碼:

 

赞(0)

分享創造快樂