目錄
BitMap
使用場景
1.?用戶簽到系統
2.?用戶行為標記
3.?布隆過濾器(Bloom Filter)
BitMap介紹?
Redis中的使用?
Redis功能示例
添加:?
獲取:
批量獲取:
?java中實現
?統計本月連續簽到次數
?UV統計
?UV 統計的核心需求
使用?HyperLogLog
UV 統計的常見場景
場景 1:每日 UV 統計
場景 2:月度 UV 統計
BitMap
使用場景
在開發中,Bitmap
?經常被用于以下場景:
1.?用戶簽到系統
場景描述:
用戶每天簽到一次,系統需要記錄用戶每月的簽到情況,并支持快速查詢連續簽到天數、總簽到天數等。
實現方式:
-
使用一個?
Bitmap
,每一位代表一天(1表示簽到,0表示未簽到)。 -
例如,用戶ID為1的用戶在2023年10月的簽到記錄可以用一個31位的?
Bitmap
?表示。
優點:
-
存儲空間極小:一個月的簽到記錄只需要4字節(32位)。
-
查詢效率高:可以通過位運算快速計算連續簽到天數、總簽到天數等。
2.?用戶行為標記
場景描述:
系統需要標記用戶是否完成了某些行為(例如是否閱讀了某篇文章、是否參與了某個活動等)。
實現方式:
-
使用一個?
Bitmap
,每一位代表一個行為(1表示完成,0表示未完成)。 -
例如,用戶ID為1的用戶完成了行為A、B、D,可以用?
0b1101
?表示。
優點:
-
節省存儲空間:一個用戶的所有行為標記可以用一個整數表示。
-
支持快速查詢:通過位運算可以快速判斷用戶是否完成了某個行為。
3.?布隆過濾器(Bloom Filter)
場景描述:
布隆過濾器是一種概率型數據結構,用于快速判斷某個元素是否存在于一個集合中(可能存在誤判,但不會漏判)。
實現方式:
-
使用一個?
Bitmap
?作為布隆過濾器的底層存儲結構。 -
通過多個哈希函數將元素映射到?
Bitmap
?的不同位置,并將這些位置標記為1。
優點:
-
空間效率極高:適合海量數據的去重和查詢。
-
查詢速度快:時間復雜度為 O(1)。
BitMap介紹?
?
?如果是使用表來儲存,需要耗費大量的內存,數據庫壓力山大
因此我們換一種方式來存儲,一個月最多有31天,因此,如果某一天簽到了,那么對應的位為1,沒有則為0。這種方式只需要31bit,也就是8字節,大大節省了空間。
Redis中的使用?
Redis功能示例
添加:?
儲存為11100111
獲取:
批量獲取:
u2中的u表示儲存的為無符號,2表示只截取兩個比特位,截取結果為11,轉化為十進制就是3
?java中實現
public Result sign() {// 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 獲取日期LocalDateTime now = LocalDateTime.now();// 拼接用戶和日期變成keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
// String key = "sign:"+userId+keySuffix;String key = USER_SIGN_KEY+userId+keySuffix;// 獲取今天是本月的第幾天int dayOfMonth = now.getDayOfMonth();// 寫入Redis setbit key offset 1stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true); // 注意這里需要減一因為在儲存中字節是從0開始的return Result.ok();}
?統計本月連續簽到次數
@Overridepublic Result signCount() {// 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 獲取日期LocalDateTime now = LocalDateTime.now();// 拼接用戶和日期變成keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
// String key = "sign:"+userId+keySuffix;String key = USER_SIGN_KEY+userId+keySuffix;// 獲取今天是本月的第幾天int dayOfMonth = now.getDayOfMonth();//獲取本月為止的所有的簽到記錄,返回的是一個十進制的數字 BITFIELD key GET udayOfMonth 0List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)) // 子命令.valueAt(0));if(result == null || result.isEmpty()){return Result.ok(0);}// 為什么需要 get(0)?get(0) 是從 List<Long> 中獲取第一個元素。// stringRedisTemplate.opsForValue().bitField(...) 返回的是一個 List<Long>,// 即使你只請求了一個值,它也會以列表的形式返回。// 因此,result.get(0) 獲取的是這個列表中的第一個元素,也就是你請求的簽到記錄的值。Long num = result.get(0);if(num == null || num == 0){return Result.ok(0);}// 遍歷循環int cnt = 0;while(cnt < dayOfMonth){if ((num & 1) == 0) {break;}cnt++;// 把數字右移一位,拋棄最后一個bit位,繼續下一個bit位num >>>=1;}return Result.ok(cnt);}
?UV統計
在 Redis 中,UV(Unique Visitor)統計?是指統計某個時間段內訪問某個資源的獨立用戶數量。UV 統計是許多應用場景(如網站訪問量統計、廣告點擊統計等)中的核心需求。Redis 提供了多種數據結構和方法來實現高效的 UV 統計。
以下是 Redis 中 UV 統計的相關知識點介紹:
?UV 統計的核心需求
-
去重:同一個用戶在同一時間段內的多次訪問只算作一次。
-
高效存儲:需要支持海量用戶的統計。
-
快速查詢:能夠快速獲取某個時間段內的 UV 數據。
使用?HyperLogLog
原理:
-
HyperLogLog 是一種概率算法,用于估算大量數據的基數(去重后的數量)。
-
它通過極小的存儲空間(每個 HyperLogLog 鍵只需要 12 KB)來統計 UV。
命令:
-
PFADD key user_id
:將用戶 ID 添加到 HyperLogLog 中。 -
PFCOUNT key
:獲取 UV 的估算值。
優點:
-
存儲空間極小,適合海量用戶的 UV 統計。
-
查詢速度快。
缺點:
-
結果是估算值,存在一定的誤差(標準誤差約為 0.81%)
UV 統計的常見場景
場景 1:每日 UV 統計
需求:
-
統計每天的獨立訪問用戶數。
實現:
-
使用?
HyperLogLog
,每天創建一個新的鍵(例如?uv:2023-10-01
),將當天的用戶 ID 添加到鍵中。 -
每天結束時,使用?
PFCOUNT
?獲取當天的 UV 值。
場景 2:月度 UV 統計
需求:
-
統計每月的獨立訪問用戶數。
實現:
-
使用?
HyperLogLog
,將整個月的用戶 ID 添加到同一個鍵中(例如?uv:2023-10
)。 -
每月結束時,使用?
PFCOUNT
?獲取當月的 UV 值。