使用hashmap優化壓縮Redis內存使用 原

使用hashmap優化壓縮Redis內存使用


背景

近來公司內部dsp架構升級,需要能夠根據請求中的設備id實時的獲取到該設備的用戶畫像相關信息,於是選用每天使用離線任務把用戶數據灌入redis裏面,供線上服務實時查詢。

需求評估

需求是篩選出最近一個月活躍的設備,將其用戶畫像屬性灌入redis中。於是篩選出30天的活躍設備總量有24億。這麼大的量如果直接使用設備id作爲key直接寫入redis,按value佔用16字節來算,大概要用230G內存的redis集羣,這成本還是比較可觀的。

內存使用評估方法

爲了評估我們設計的方法具體將佔用多大的內存,上網查了寫資料,發現跟redis內存容量使用和優化相關的只有這一篇文章《REDIS內存容量的預估和優化》,其他都是相互轉載的,雖然不知道作者是誰,但是向作者致敬。

方案設計

大家知道Hash表空間大小和Key的個數決定了衝突率(或者用負載因子衡量),再合理的範圍內,key越多自然hash表空間越大,消耗的內存自然也會很大。再加上大量指針本身是長整型,所以內存存儲的膨脹十分可觀。所以主要優化思路是考慮如何把key的個數減少。

在網上搜了下相關的博客,發現了這篇文章《Redis百億級Key存儲方案》,同樣也是相互轉載的特別多,已不知出處,裏面bucket的思路啓發了我,這裏深深感謝作者。

原文的意思是設計一個映射函數,將設備id經過變換,映射到一個桶id上,桶的數量比原始數據的數量小2-3個數量級,而映射函數的作用是能夠儘可能平均的將key映射到桶id上,使用桶id作爲redis中的key,value使用hashmap結構,原始數據中的key和value作爲hashmap中的subkey和value。這樣能夠大幅減少redis中全局key的數量。所以這裏這個映射函數是一個比較核心的東西,要設計一個好的映射函數是很困難的。這裏我經過多次嘗試,方案如下:

選用CRC32算法對設備id做映射,再將結果對$2^{26}$取模作爲桶id。這樣能夠將桶的數量控制在$2^{26}$ 這個數量上,同時使桶內的subkey分佈儘可能均勻,減少數據傾斜的情況出現。同時使用BKDRHash算法對作爲subkey的device id進行變換和壓縮,從36字節變爲4字節。

但是我們使用引用的第一篇文章中的公式來算一下: 首先我們有24億條數據,這裏桶的個數定爲$2^{26}$個,平均每個桶裏有35個子key。爲了存儲這$2^{26}$個key,我們需要key的長度爲4個字節。 key字節數爲4 + 9 ~ 16字節 subkey字節數爲4字節 value字節數爲16字節 則單個hashmap的大小爲

35 × (4 + 16 + 3) + 2 = 807 字節 ~ 1024

總內存大小爲

2 ^ 26 × (16 + 16 + 16 + 1024) + 2 ^ 26 × 4 = 67 G

可以看到總內存只使用了67G

總結

1.經過了上面的優化,從230G的內存佔用到67G,內存使用減少了3.5倍,滿滿的成就感啊。 2.利用redis的hashmap可以極大的減少key的數量,和減少內存使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章