文章目录
- 前言
- HyperLogLog介绍
- HyperLogLog指令使用
- 使用场景:UV统计
前言
- Redis除了常见的五种数据类型之外,其实还有一些少见的数据结构,如Geo,HyperLogLog等。虽然它们少见,但是作用却不容小觑。本文将介绍HyperLogLog指令的语法和使用场景。
HyperLogLog介绍
- HyperLogLog是Redis提供的一种不准确(标准误差为0.81%)的去重计数方案。
- 提到去重计数,第一个想到的数据结构就是集合set,set集合可以保存数据,并用scard准确地返回集合中的数据条数,但是若数据量很大,那么使用set集合就需要相当大的存储空间,这显然不是我们想看到的结果,而对于某些统计其实并不需要特别精确的结果。
- 对于这种情况,HyperLogLog就可以发挥大用,HyperLogLog的优势在于无论存储的数据量有多大,它都只需要12KB的存储空间(可以计算接近2^64个不同的基数)。当然它只能统计数据集的个数,而不直接存放元素,不能试图用它保存元素。另外HyperLogLog对数据集的统计也不是完全准确的,存在一定误差,可能比实际情况多或少。
HyperLogLog指令使用
-
增加计数: pfadd key element [element…]
-
查询计数: pfcount key
-
将多个HyperLogLog合并到一个HyperLogLog中: pfmerge destkey sourcekey [sourcekey …]
使用场景:UV统计
-
HyperLogLog最适合的使用场景就是做UV的统计了,简直量身定制一般。一个爆款页面的UV可能有几千万,使用HyperLogLog可以节约存储空间,并且UV的统计允许存在一定的误差。
补充:
- pv(page view,浏览量)
- 页面的浏览次数,衡量网站用户访问的网页数量;
- 用户每打开一个页面就记录1次,多次打开同一页面则浏览量累计。
- uv(unique visitor,独立访客)
- 1天内访问某站点的人数(以cookie为依据);
- 1天内同一访客的多次访问只计为1个访客。
- ip(独立ip)
- 指1天内使用不同ip地址的用户访问网站的数量;
- 同一IP不管访问了几个页面,独立IP数均为1。
- pv(page view,浏览量)
-
代码实现: 在代码中提供了三个方法,分别对应pfadd添加页面的UV,pfcount获取页面的UV统计结果和pfmerge合并指定页面UV。
public class UVByHyperLogLog { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1"); jedis.del(UV_PAGE_1); jedis.del(UV_PAGE_2); jedis.del(UV_PAGE_1_2); for (int i = 0; i < 10000; i++) { String userid = "userid:"+Math.random()*100000; addCount(UV_PAGE_1,userid,jedis); } System.out.println("页面一的UV:"+getCount(UV_PAGE_1,jedis)); jedis.del(UV_PAGE_2); for (int i = 0; i < 5000; i++) { String userid = "userid:"+Math.random()*100000; addCount(UV_PAGE_2,userid,jedis); } System.out.println("页面二的UV:"+getCount(UV_PAGE_2,jedis)); //页面一和页面二合并后的UV 合并与的UV不等于两个UV的相加,一样是不精确的 mergeCount(UV_PAGE_1_2, new String[]{UV_PAGE_1, UV_PAGE_2},jedis); System.out.println("合并后两个页面的UV总数"+getCount(UV_PAGE_1_2,jedis)); } private static final String UV_PAGE_1 = "UV_PAGE_1"; private static final String UV_PAGE_2 = "UV_PAGE_2"; private static final String UV_PAGE_1_2 = "UV_PAGE_1_2"; /** * 向HyperLogLog添加数据 * @param key UV_KEY * @param userId 用户Id * @param jedis */ public static void addCount(String key, String userId,Jedis jedis){ jedis.pfadd(key,userId); } /** * 返回统计的结果 * @param key * @param jedis * @return */ public static long getCount(String key,Jedis jedis){ return jedis.pfcount(key); } /** * 将多个pf计数合并为一个pf计数 * @param newKey 合并后的新HyperLogLog的key * @param keys 要合并的HyperLogLog的key * @param jedis * @return */ public static void mergeCount(String newKey,String[] keys,Jedis jedis){ jedis.pfmerge(newKey,keys); } }
-
测试结果:在上述main方法中,第一个for循环,模拟10000个用户访问页面一,第二个for循环模拟5000个用户访问页面二,通过HyperLogLog进行UV统计,输出统计结果,最后合并两个页面的统计值,观察结果后发现,HyperLogLog统计后的结果与实际结果存在误差,但是在数据量很大的情况下,这点误差误伤大雅。: