共计 1399 个字符,预计需要花费 4 分钟才能阅读完成。
序
本文主要研究一下 redis 的 HyperLogLog 的用场
相关命令
pfadd
每添加一个元素的复杂度为 O(1)
127.0.0.1:6379> pfadd uv0907 uid1 uid2 uid3
(integer) 1
添加元素到 HyperLogLog 中,如果内部有变动返回 1,没有返回 0
pfcount
作用域单个 HyperLogLog 时,复杂度为 O(1),作用于多个 HyperLogLog 时,复杂度为 O(N)
127.0.0.1:6379> pfcount uv0907
(integer) 3
返回该 HyperLogLog 的近似基数,如果是指定多个 HyperLogLog 则返回的是他们的并集的近似基数
pfmerge
复杂度为 O(N),N 为合并后的 HyperLogLog 数量
127.0.0.1:6379> pfadd uv0906 uid1 uid4 uid5
(integer) 1
127.0.0.1:6379> pfmerge uv0607 uv0906 uv0907
OK
127.0.0.1:6379> pfcount uv0607
(integer) 5
合并指定的 HyperLogLog 到新的 HyperLogLog 中
使用场景
HyperLogLog 是 Probabilistic data Structures 的一种,这类数据结构的基本大的思路就是使用统计概率上的算法,牺牲数据的精准性来节省内存的占用空间及提升相关操作的性能。最典型的使用场景就是统计网站的每日 UV。实例如下:
@Test
public void testUv(){
String uv1 = “uv96”;
String uv2 = “uv97”;
IntStream.rangeClosed(1,100)
.forEach(i -> {
System.out.println(i);
redisTemplate.opsForHyperLogLog()
.add(uv1,”user”+i);
redisTemplate.opsForHyperLogLog()
.add(uv2,”user”+i/2);
});
long uv1Count = redisTemplate.opsForHyperLogLog().size(uv1);
System.out.println(uv1Count);
long uv2Count = redisTemplate.opsForHyperLogLog().size(uv2);
System.out.println(uv2Count);
String uv1uv2 = “uv67”;
Long uv1uv2Count = redisTemplate.opsForHyperLogLog().union(uv1uv2,uv1,uv2);
System.out.println(uv1uv2Count);
Long realCount = redisTemplate.opsForHyperLogLog().size(uv1uv2);
System.out.println(realCount);
}
小结
redis 的 HyperLogLog 特别是适合用来对海量数据进行 unique 统计,对内存占用有要求,而且还能够接受一定的错误率的场景。
对于 union 操作由于是 O(N),在海量数据层面需要注意慢查询问题。
doc
hyperloglog
pfadd
pfcount
pfmerge
HyperLogLogs in Redis
hyperloglog 的 java 版使用