如果我們要實現記錄網站每天訪問的獨立IP數量這樣的一個功能
集合實現:
使用集合來儲存每個訪客的 IP ,通過集合性質(集合中的每個元素都各不相同)來得到多個獨立 IP ,
然后通過調用 SCARD 命令來得出獨立 IP 的數量。
舉個例子,程序可以使用以下代碼來記錄 2014 年 8 月 15 日,每個網站訪客的 IP :
ip = get_vistor_ip()
SADD '2014.8.15::unique::ip' ip
然后使用以下代碼來獲得當天的唯一 IP 數量:
SCARD '2014.8.15::unique::ip'
集合實現的問題
使用字符串來儲存每個 IPv4 地址最多需要耗費 15 字節(格式為 'XXX.XXX.XXX.XXX' ,比如
'202.189.128.186')。
下表給出了使用集合記錄不同數量的獨立 IP 時,需要耗費的內存數量:
獨立 IP 數量一天一個月一年
一百萬15 MB 450 MB 5.4 GB
一千萬150 MB 4.5 GB 54 GB
一億1.5 GB 45 GB 540 GB
隨着集合記錄的 IP 越來越多,消耗的內存也會越來越多。
另外如果要儲存 IPv6 地址的話,需要的內存還會更多一些
為了更好地解決像獨立 IP 地址計算這種問題,
Redis 在 2.8.9 版本添加了 HyperLogLog 結構。
HyperLogLog介紹
HyperLogLog 可以接受多個元素作為輸入,並給出輸入元素的基數估算值:
• 基數:集合中不同元素的數量。比如 {'apple', 'banana', 'cherry', 'banana', 'apple'} 的基數就是 3 。
• 估算值:算法給出的基數並不是精確的,可能會比實際稍微多一些或者稍微少一些,但會控制在合
理的范圍之內。
HyperLogLog 的優點是,即使輸入元素的數量或者體積非常非常大,計算基數所需的空間總是固定
的、並且是很小的。
在 Redis 里面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基
數。這和計算基數時,元素越多耗費內存就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以
HyperLogLog 不能像集合那樣,返回輸入的各個元素。
將元素添加至 HyperLogLog
PFADD key element [element ...]
將任意數量的元素添加到指定的 HyperLogLog 里面。
這個命令可能會對 HyperLogLog 進行修改,以便反映新的基數估算值,如果 HyperLogLog 的基數估算
值在命令執行之后出現了變化, 那么命令返回 1 , 否則返回 0 。
命令的復雜度為 O(N) ,N 為被添加元素的數量。
返回給定 HyperLogLog 的基數估算值
PFCOUNT key [key ...]
當只給定一個 HyperLogLog 時,命令返回給定 HyperLogLog 的基數估算值。
當給定多個 HyperLogLog 時,命令會先對給定的 HyperLogLog 進行並集計算,得出一個合並后的
HyperLogLog ,然后返回這個合並 HyperLogLog 的基數估算值作為命令的結果(合並得出的
HyperLogLog 不會被儲存,使用之后就會被刪掉)。
當命令作用於單個 HyperLogLog 時, 復雜度為 O(1) , 並且具有非常低的平均常數時間。
當命令作用於多個 HyperLogLog 時, 復雜度為 O(N) ,並且常數時間也比處理單個 HyperLogLog 時要
大得多。
PFADD 和 PFCOUNT 的使用示例
redis> PFADD unique::ip::counter '192.168.0.1'
(integer) 1
redis> PFADD unique::ip::counter '127.0.0.1'
(integer) 1
redis> PFADD unique::ip::counter '255.255.255.255'
(integer) 1
redis> PFCOUNT unique::ip::counter
(integer) 3
合並多個 HyperLogLog
PFMERGE destkey sourcekey [sourcekey ...]
將多個 HyperLogLog 合並為一個 HyperLogLog ,合並后的 HyperLogLog 的基數估算值是通過對所有
給定 HyperLogLog 進行並集計算得出的。
命令的復雜度為 O(N) , 其中 N 為被合並的 HyperLogLog 數量, 不過這個命令的常數復雜度比較高。
PFMERGE 的使用示例
redis> PFADD str1 "apple" "banana" "cherry"
(integer) 1
redis> PFCOUNT str1
(integer) 3
redis> PFADD str2 "apple" "cherry" "durian" "mongo"
(integer) 1
redis> PFCOUNT str2
(integer) 4
redis> PFMERGE str1&2 str1 str2
OK
redis> PFCOUNT str1&2
(integer) 5
HyperLogLog 實現獨立 IP 計算功能
獨立 IP 數量一天一個月一年一年(使用集合)
一百萬12 KB 360 KB 4.32 MB 5.4 GB
一千萬12 KB 360 KB 4.32 MB 54 GB
一億12 KB 360 KB 4.32 MB 540 GB
下表列出了使用 HyperLogLog 記錄不同數量的獨立 IP 時,需要耗費的內存數量:
可以看到,要統計相同數量的獨立 IP ,HyperLogLog 所需的內存要比集合少得多。