1. Redis 的 BitMap 概述
在 Redis 中,BitMap 并非一種獨立的數據結構,而是基于 String 類型數據結構實現的一種存儲方式。由于 String 類型的最大上限是 512M,換算成 bit 位就是 2^32 個,這決定了 BitMap 可操作的最大范圍。BitMap 非常適合用于處理大量的布爾值,能以極小的空間存儲大量的標志位信息,常用于簽到統計、活躍用戶統計等場景。
2. BitMap 常用操作命令
SETBIT:用于向指定位置(offset)存入一個 0 或 1,例如?SETBIT key offset value
。
GETBIT:獲取指定位置(offset)的 bit 值,即?GETBIT key offset
。
BITCOUNT:統計 BitMap 中值為 1 的 bit 位的數量,命令為?BITCOUNT key
。
BITFIELD:可對 BitMap 中 bit 數組的指定位置(offset)的值進行查詢、修改、自增等操作。
BITFIELD_RO:獲取 BitMap 中 bit 數組,并以十進制形式返回。
BITOP:能將多個 BitMap 的結果做位運算(與、或、異或),如?BITOP AND result_key key1 key2
。
BITPOS:查找 bit 數組中指定范圍內第一個 0 或 1 出現的位置,例如?BITPOS key 1
。
3. 基于 BitMap 的簽到功能實現
簽到實現
- 業務邏輯:獲取當前登錄用戶的 ID 和日期,根據用戶 ID 和日期拼接 Redis 的 key,確定今天是本月的第幾天,然后使用?
SETBIT
?命令將該位置的 bit 位設置為 1 表示簽到。
@Override
public Result sign() {Long userId = UserHolder.getUser().getId();LocalDateTime now = LocalDateTime.now();String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;int dayOfMonth = now.getDayOfMonth();stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}
簽到統計
- 業務邏輯:同樣先獲取當前登錄用戶的 ID 和日期,拼接 Redis 的 key 并確定今天是本月的第幾天。使用?
BITFIELD
?命令獲取本月截止今天為止的所有簽到記錄,返回一個十進制數字。通過循環對該數字進行位運算,從右向左逐位檢查,統計連續簽到的天數,直到遇到第一個 0 為止。
@Override
public Result signCount() {Long userId = UserHolder.getUser().getId();LocalDateTime now = LocalDateTime.now();String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;int dayOfMonth = now.getDayOfMonth();List<Long> result = stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if (result == null || result.isEmpty() || result.get(0) == null || result.get(0) == 0) {return Result.ok(0);}Long num = result.get(0);int count = 0;while (num > 0) {if ((num & 1) == 0) {break;} else {count++;}num >>>= 1;}return Result.ok(count);
}
四、BitMap總結
Redis 的 BitMap 利用 String 類型的特性,以高效的方式處理大量布爾值信息。在簽到功能的實現中,通過合理使用?SETBIT
?和?BITFIELD
?等命令,能輕松完成簽到記錄和簽到統計的操作,不僅節省了存儲空間,還提高了操作效率。
五、UV統計
UV:全稱Unique Visitor,也叫獨立訪客量,是指通過互聯網訪問、瀏覽這個網頁的自然人。1天內同一個用戶多次訪問該網站,只記錄1次。
PV:全稱Page View,也叫頁面訪問量或點擊量,用戶每訪問網站的一個頁面,記錄1次PV,用戶多次打開頁面,則記錄多次PV。往往用來衡量網站的流量。
在 UV 統計場景中,由于需要統計大量獨立訪客,使用傳統方式存儲所有訪客信息會占用大量內存。而 Redis 的 HyperLogLog 以其低內存占用和可接受的誤差,成為了 UV 統計的理想選擇,能夠高效地完成大規模數據的基數統計任務。
HyperLogLog(HLL)是從 Loglog 算法派生的概率算法,用于確定非常大的集合的基數(集合中不同元素的數量),無需存儲集合中的所有值。
Redis 中 HyperLogLog 的特點
????????數據結構:基于 string 結構實現。
????????內存占用:單個 HLL 的內存永遠小于 16kb,內存占用極低。
????????誤差:測量結果是概率性的,存在小于 0.81%的誤差,但對于 UV 統計來說,此誤差可忽略不計。
對于百萬級別的數據,使用 HyperLogLog 進行統計時,內存占用僅十幾 kb,充分體現了 HyperLogLog 在處理大規模數據基數統計時內存占用低的優勢。