HyperLogLog
假設(shè)有個(gè)千萬日活的統(tǒng)計(jì)系統(tǒng),需要統(tǒng)計(jì)系統(tǒng)每天的UV。如果是你的話你該怎么設(shè)計(jì)?
如果統(tǒng)計(jì) PV 那非常好辦,使用string的incr就搞定了。
但是 UV 不一樣,它要去重,同一個(gè)用戶一天之內(nèi)的多次訪問請(qǐng)求只能計(jì)數(shù)一次。這就要求每一個(gè)網(wǎng)頁請(qǐng)求都需要帶上用戶的 ID,無論是登陸用戶還是未登陸用戶都需要一個(gè)唯一 ID 來標(biāo)識(shí)。
我們第一反應(yīng)就是為每一個(gè)頁面搞一個(gè)獨(dú)立的 set 集合來存儲(chǔ)所有當(dāng)天訪問過此頁面的用戶 ID。當(dāng)一個(gè)請(qǐng)求過來時(shí),我們使用 sadd 將用戶 ID 塞進(jìn)去就可以了。通過 scard 可以取出這個(gè)集合的大小,這個(gè)數(shù)字就是這個(gè)頁面的 UV 數(shù)據(jù)。沒錯(cuò),這是一個(gè)非常簡(jiǎn)單的方案。
存在問題:
①占用內(nèi)存,set集合里有1000萬條數(shù)據(jù),如果一個(gè)用戶ID占32個(gè)字節(jié),一天就320M,這是非??植赖?/p>
②性能差,當(dāng)數(shù)據(jù)量大時(shí),sadd性能會(huì)下降
其實(shí)對(duì)于千萬日活系統(tǒng),老板需要的數(shù)據(jù)不需要太精確,1001萬和1002萬對(duì)于老板決策沒有太大影響。
Redis 提供了 HyperLogLog 數(shù)據(jù)結(jié)構(gòu)就是用來解決這種統(tǒng)計(jì)問題的。HyperLogLog 提供不精確的去重計(jì)數(shù)方案,雖然不精確但是也不是非常不精確,標(biāo)準(zhǔn)誤差是 0.81%,這樣的精確度已經(jīng)可以滿足上面的 UV 統(tǒng)計(jì)需求了。
常用命令
127.0.0.1:6379> pfadd uv user1 ## 將元素參數(shù)添加到 HyperLogLog 數(shù)據(jù)結(jié)構(gòu)中
(integer) 1
127.0.0.1:6379> pfadd uv user2
(integer) 1
127.0.0.1:6379> pfadd uv user1
(integer) 0
127.0.0.1:6379> pfcount uv
(integer) 2