redis占用内存过低_使用多种数据结构优化Redis 内存占用
背景
廣告平臺 adx 在處理曝光/點擊上報時,使用 redis 的 setnx 命令去重,其邏輯如下
現(xiàn)在的需求就是減少此項去重業(yè)務所占用的 redis 內存
技術方案
方案一:簡單通用
- value 置為 0,這樣每個 key 節(jié)約了 8 字節(jié)
- 說明:雖然時間戳是 10 位數(shù)字,但 redis 會以 8 字節(jié)整數(shù)形式進行存儲
- 簡化 key,目前 key 的前綴太長,可以簡化為 te/tc,表示曝光/點擊上報
- 用廣告序號來構造 key,而不是用廣告位 id 構造 key,這樣還能兼容一個廣告位填充多個廣告的情況
- 縮短過期時間
方案二:使用 hash
一次廣告請求,實際上會導致多次廣告上報
- 可能返回了多個廣告
- 一個廣告可能上報曝光和點擊
如果一次下發(fā)了 10 個廣告,每個廣告都上報了曝光和點擊,則要在 redis 里創(chuàng)建 20 個 key,這 20 個 key 的請求 id 是完全一樣的
這種情況可以用 hash 來節(jié)約內存
- 使用請求 id 做 key
- 上報類型+廣告序號 做 field
示例如下
hsetnx t:1ad72e9d0171ac12041c271000000000 e99 0方案三:使用 set
我們來考察一下 redis 的 set,通過
sadd key member方法可以往一個集合里添加元素,如果添加成功,返回 1,添加不成功,說明元素已存在,返回 0。可見,set 也可以用來做去重
當 set 元素不多且元素值為整數(shù)時,redis 會使用 intset 來實現(xiàn) set;否則使用 hashtable
方案二使用 hash 結構,field 是 e/c 拼接廣告序號;改造成整數(shù)的話,曝光可以用 2*序號,點擊可以用 2*序號+1,從性能考慮也可以用位操作
例如,廣告請求 id = 0a80114e01717684281e000249ef,填充了 5 個廣告,全部曝光,第一個廣告點擊,則有如下操作
String reqid = "0a80114e01717684281e000249ef";int member;// 第一個廣告曝光member = 0 * 2;if (sadd(reqid, member) == 1) // 曝光未重復; do something...// 第一個廣告點擊member = 0 * 2 + 1;if (sadd(reqid, member) == 1) // 點擊未重復; do something...// 第二個廣告曝光member = 1 * 2;if (sadd(reqid, member) == 1) // 曝光未重復; do something...仔細研究intset ,可以發(fā)現(xiàn)和我們的業(yè)務需求非常的匹配
- 當集合元素全部是整數(shù),且數(shù)量少于 512(可通過 set-max-intset-entries 配置) 個,redis 使用 intset 而不是 hashtable 來實現(xiàn) set
- intset 保存的整數(shù)可以是 2 字節(jié),4 字節(jié)或者 8 字節(jié),而我們的廣告業(yè)務下發(fā)的廣告數(shù)量肯定不會超過65535個,也就是說在 redis 里保存廣告序號只需要用 2 字節(jié)的整數(shù)
- intset 判斷元素是否存在使用二分查找法,時間復雜度是 O(logn),hashtable 的時間復雜度基本是 O(1),也就是說,intset 雖然內存消耗低,但是響應會慢一點,對于我們的業(yè)務來說,一個 set 里的元素平均只有 3-5 個,用二分查找的性能完全可以接受
- 即便是對于一些填充了幾十個創(chuàng)意的廣告請求,判斷曝光/點擊是否重復可能會慢點,但是我們這個操作本身就是異步的,慢一點可以接受
方案四:使用 bitmap 位圖
無論是使用 hash 還是 set ,都基于一個前提:認為一次廣告請求會產生多次上報;但這個前提并非絕對,很多情況是一次請求只返回了一個廣告,最終只觸發(fā)了曝光,沒有觸發(fā)點擊,這種情況使用 hash/set 是不是反而消耗了更多內存呢?
最簡單的做法是,如果本次廣告請求只返回了一個廣告,我們使用 setnx 來設置一個 key——轉了一圈回到原點了嗎?如果又發(fā)生了點擊該怎么辦?
對于這種情況,有沒有什么更好的方案呢?有沒有一種方案,可以用一個 value 來表示多個廣告是否曝光/點擊過?
經(jīng)過研究,位圖似乎是個不錯的方案
- 若廣告序號=n,一共填充了 t 個廣告,那么位圖的第 n 位表示廣告是否曝光,第 t + n 位表示廣告是否點擊
例如: 本次下發(fā)了 10 個廣告,當前曝光的廣告序號為 2,要判斷該廣告是否曝光過
// 第三個廣告曝光,廣告序號為 2int offset = 2;boolean result = setbit(key, offset, true);if (result) { // 重復曝光} else { // 首次曝光}總結
降低 redis 內存占用的方法
- 減少 key 的數(shù)量
- 提取出 key 里的相同數(shù)據(jù),使用 hash 來映射 key 里的不同數(shù)據(jù)
- 要特別注意的是,如果 hash 的字段很多,就要考慮負載均衡的問題:因為我們的 redis 環(huán)境是分布式的,如果個別 hash 的字段太多,會導致負載集中到承載該 hash 的服務器
- 降低 key 本身的內存占用
- key 的構造方式是否存在優(yōu)化空間
- 降低 value 的內存占用
- 如果 value 的值沒有實際意義,建議存為 0
- 合理的選擇數(shù)據(jù)結構
總結
以上是生活随笔為你收集整理的redis占用内存过低_使用多种数据结构优化Redis 内存占用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python编译过程和解释过程的不同_P
- 下一篇: python公里转海里_海里、公里、英里