Redis統(tǒng)計獨立用戶訪問量的四種方案
在網(wǎng)站分析、廣告監(jiān)測、推薦系統(tǒng)等場景中,獨立用戶訪問量(UV,Unique Visitor) 是一個核心指標(biāo)。UV 的關(guān)鍵在于去重——同一個用戶多次訪問只計一次。
Redis 提供了多種數(shù)據(jù)結(jié)構(gòu)來高效實現(xiàn) UV 統(tǒng)計,各有優(yōu)劣。本文將詳細(xì)對比 Set、Bitmap、HyperLogLog、incr + 日期維度(即用戶提到的兩種方式)四種方案,并通過流程圖和代碼示例幫助你選型。
一、方案概覽(附選型流程圖)

二、方案一:Set 集合(精確去重)
最直觀的方法:每個統(tǒng)計周期(如一天)維護(hù)一個 Set,將每個訪問過的用戶 ID 加入 Set,最后用 SCARD 獲取基數(shù)。
# 示例:用戶 1001 訪問首頁
redis.sadd("uv:home:2025-04-15", "user_1001")
# 獲取當(dāng)天 UV
uv = redis.scard("uv:home:2025-04-15")優(yōu)點:精確、支持用戶 ID 任意類型(字符串/整數(shù))。
缺點:內(nèi)存占用高,每個用戶 ID 都需要存儲一份(例如 1000 萬用戶,每個 ID 按 30 字節(jié)算,需約 300MB)。
適用:用戶量?。?lt; 百萬級)或必須精確統(tǒng)計的場景。
三、方案二:Bitmap(位圖法,精確且內(nèi)存極?。?/h2>
當(dāng)用戶 ID 是整數(shù)且相對連續(xù)(如自增 user_id)時,可以用 Bitmap 將每個 user_id 映射到位偏移量,存在則置 1。
# 用戶 ID=1001 訪問,設(shè)置第 1001 位為 1
redis.setbit("uv:home:2025-04-15", 1001, 1)
# 統(tǒng)計當(dāng)日 UV(統(tǒng)計 1 的個數(shù))
uv = redis.bitcount("uv:home:2025-04-15")
內(nèi)存計算:如果有 1 億用戶,只需 1億 bit ≈ 12 MB,比 Set 節(jié)省數(shù)十倍。
優(yōu)點:精確、內(nèi)存極小、性能高(bitcount 時間復(fù)雜度 O(n) 但 Redis 做了優(yōu)化)。
缺點:用戶 ID 必須為整數(shù)且不太稀疏(若 ID 最大為 10 億,但實際只有 100 萬用戶,依然會占用 125MB 的連續(xù)空間,造成浪費)。
適用:用戶 ID 是自增整數(shù)、最大 ID 可控(如 2^32 以內(nèi))、對內(nèi)存敏感且要求精確的場景。
四、方案三:HyperLogLog(近似去重,誤差 0.81%)
你提到的 HyperLogLog 是一種概率性數(shù)據(jù)結(jié)構(gòu),用 12KB 固定內(nèi)存即可統(tǒng)計上億級別的 UV,誤差率約為 0.81%。
# 添加元素
redis.pfadd("uv:home:2025-04-15", "user_1001", "user_1002")
# 獲取近似 UV
uv = redis.pfcount("uv:home:2025-04-15")
原理:通過哈希函數(shù)將元素映射為二進(jìn)制串,觀察低位連續(xù)零的個數(shù)來估計基數(shù)。
優(yōu)點:內(nèi)存固定(12KB),性能極高(O(1) 添加),適合海量數(shù)據(jù)。
缺點:不精確(誤差 ±0.81%),無法取出具體有哪些用戶(只能計數(shù)),不適合敏感計費場景。
適用:大屏展示、趨勢分析、非精準(zhǔn)營銷統(tǒng)計等可容忍誤差的場景。
五、方案四:incr + 日期維度(你提到的“incr自增”)
嚴(yán)格來說,單純使用 INCR 無法實現(xiàn)獨立用戶去重,因為 INCR 是累加計數(shù)器,每次訪問都 +1,得到的是 PV(頁面訪問量),不是 UV。
# 這樣得到的是 PV,不是 UV
redis.incr("pv:home:2025-04-15")
如何用 incr 輔助 UV?
通常做法是 incr + Set/Bitmap/HLL 組合:
- 用 Set 或 HLL 存儲獨立用戶(保證去重)
- 同時用 incr 記錄總訪問次數(shù)(PV)
# 記錄 PV
redis.incr("pv:home:2025-04-15")
# 記錄 UV(使用 HLL)
redis.pfadd("uv:home:2025-04-15", user_id)
所以,你提到的“incr 通過自增方式判斷用戶的訪問量”并不適用于 UV,應(yīng)理解為 PV 統(tǒng)計。但為了貼合你的原文,我們修正說明:incr 適合 PV,UV 必須依賴去重結(jié)構(gòu)。
六、四種方案對比表
| 方案 | 內(nèi)存占用 | 精確性 | 支持用戶ID類型 | 時間復(fù)雜度(寫入) | 典型應(yīng)用 |
|---|---|---|---|---|---|
| Set | O(N)(每個元素完整存儲) | 精確 | 任意 | O(1) | 小規(guī)模精確統(tǒng)計 |
| Bitmap | O(max_id) 位,連續(xù)整數(shù)時極省 | 精確 | 非負(fù)整數(shù) | O(1) | 億級整數(shù)ID,如手機號后幾位 |
| HyperLogLog | 固定 12KB | 近似(誤差 0.81%) | 任意(需哈希) | O(1) | 海量UV快速估算 |
| incr(PV) | 固定(每個key一個整數(shù)) | 精確 | 無(只是計數(shù)) | O(1) | 頁面訪問總量(非UV) |
七、實戰(zhàn)選型建議
你的用戶 ID 是整數(shù)且密集(如 user_id 從 1 到 5000 萬)
?? 首選 Bitmap,精確且內(nèi)存最小。
用戶 ID 是字符串(如 UUID、手機號),且允許 0.81% 誤差
?? 首選 HyperLogLog,12KB 內(nèi)存統(tǒng)計上億 UV。
必須精確統(tǒng)計,且用戶量較?。?lt; 500 萬)
?? 用 Set,簡單可靠。
既要 PV 又要 UV
?? 組合:INCR 記錄 PV + PFADD 記錄 UV(HLL)或 SADD(Set)。
數(shù)據(jù)敏感場景(如計費、反 作弊)
? 不能用 HyperLogLog,必須用 Bitmap 或 Set。
八、代碼示例:三種方案對比(Python + Redis)
import redis
r = redis.Redis(decode_responses=True)
# 模擬 100 萬個用戶 ID(字符串)
user_ids = [f"user_{i}" for i in range(1_000_000)]
# 1. Set 方式
key_set = "uv:set"
r.delete(key_set)
for uid in user_ids:
r.sadd(key_set, uid)
print(f"Set 精確 UV: {r.scard(key_set)}")
print(f"Set 內(nèi)存: {r.memory_usage(key_set) / 1024 / 1024:.2f} MB")
# 2. HyperLogLog 方式
key_hll = "uv:hll"
r.delete(key_hll)
for uid in user_ids:
r.pfadd(key_hll, uid)
print(f"HLL 近似 UV: {r.pfcount(key_hll)}")
print(f"HLL 內(nèi)存: {r.memory_usage(key_hll)} 字節(jié)") # 固定約 12KB
# 3. Bitmap 方式(假設(shè) user_id 轉(zhuǎn)為整數(shù),此處用 i 模擬)
key_bit = "uv:bitmap"
r.delete(key_bit)
for i in range(1, 1_000_001):
r.setbit(key_bit, i, 1)
print(f"Bitmap 精確 UV: {r.bitcount(key_bit)}")
print(f"Bitmap 內(nèi)存: {r.memory_usage(key_bit) / 1024 / 1024:.2f} MB")
運行結(jié)果參考(百萬級):
- Set:內(nèi)存約 30~40 MB
- HLL:12 KB
- Bitmap:0.12 MB(100 萬 bit = 0.125 MB)
九、總結(jié)
| 你的原始說法 | 修正/補充 |
|---|---|
| “incr 通過自增方式判斷用戶的訪問量” | incr 得到的是 PV(總訪問次數(shù)),不是 UV。UV 需要去重。 |
| “HyperLogLog 用來做基數(shù)統(tǒng)計,誤差很小,不適合數(shù)據(jù)敏感場景” | ? 正確。誤差約 0.81%,內(nèi)存固定 12KB,適合海量近似統(tǒng)計。 |
最終結(jié)論:
- 對精度要求不高、數(shù)據(jù)量極大 → HyperLogLog
- 需要精確、用戶 ID 為整數(shù) → Bitmap
- 需要精確、用戶 ID 為字符串且量小 → Set
- 想要統(tǒng)計 PV → incr
合理選擇數(shù)據(jù)結(jié)構(gòu),能讓你的 UV 統(tǒng)計既快又省內(nèi)存。
以上就是Redis統(tǒng)計獨立用戶訪問量的四種方案的詳細(xì)內(nèi)容,更多關(guān)于Redis統(tǒng)計獨立用戶訪問量的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
深入剖析 Redis 的三種集群方式以及實戰(zhàn)配置
本文深入解析Redis三種集群部署方式,文章包含完整的配置示例和操作指南,為Redis集群部署提供實用參考,感興趣的朋友跟隨小編一起看看吧2026-03-03
Redis如何清理過期的key以及對應(yīng)的解決方法分析
這篇文章主要介紹了Redis如何清理過期的key以及對應(yīng)的解決方法的相關(guān)資料,Redis提供了多種過期刪除策略和內(nèi)存淘汰策略,以管理緩存和臨時數(shù)據(jù),需要的朋友可以參考下2025-03-03
redis數(shù)據(jù)類型_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了redis數(shù)據(jù)類型,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
為何Redis使用跳表而非紅黑樹實現(xiàn)SortedSet
本篇文章主要介紹了為何Redis使用跳表而非紅黑樹實現(xiàn)SortedSet,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09

