使用 Redis 统计网站 UV 的方法
文章目录
- 前言
- 思路
- HyperLogLog
- 使用 Redis 命令操作
- 使用 Java 代码操作
- HyperLogLog 实现原理及特点
- 使用 Java 实现 HyperLogLog
- 小结
前言
网站 UV 就是指网站的独立用户访问量Unique Visitor
,即相同用户的多次访问需要去重。
思路
提到 UV 去重,猜大家都会想到Set
集合类。
- 使用
Set
集合是一个不错的办法,Set
里面存储用户的id
。每一个用户访问页面的时候,我们直接把id
存入Set
,最终获取Set
的size
即可。问题就是Set
的容量需要设置多大呢?如果应用是分布式的,是否需要合并操作?第一个问题其实可以通过计算来估计,如果用户量上亿的话,存储空间也是需要非常大的;第二个问题其实可以通过 Redis、DB 等存储,如 Redis 的Set
结构,DB 的唯一键。 - 我们上面提到的 DB 也是一种解决方案,不过写入量很大时,数据库压力会比较大。用户如果很多,则
row
也相应的多,且可能需要对每天的数据进行分表。在用户访问量小的情况下,可以采用该处理方式。
上面两种方式虽然可以实现统计网站 UV 的功能,但是一个比较占用内存,一个比较占用数据库资源。那我们该如何规避这两个问题呢?在这里,我们就介绍另外一种实现方法,即使用 Redis 里面的HyperLogLog
结构,且仅占用12k
的空间。
HyperLogLog
HyperLogLog
的使用比较简单,实现略复杂。我们先看一下如何利用HyperLogLog
来进行页面 UV 的统计。
使用 Redis 命令操作
# 添加元素
127.0.0.1:6379> pfadd user zhangsan lisi wangwu
# 添加成功返回1,添加失败返回0
(integer) 1
# 统计数量
127.0.0.1:6379> pfcount user
# 返回现在数量
(integer) 3
# 再生成一个pfkey
127.0.0.1:6379> pfadd user2 zhangsan2 lisi2 wangwu
(integer) 1
127.0.0.1:6379> pfcount user2
(integer) 3
# pfmerge会将后面pfkey中的值合并到前面的pfkey中
127.0.0.1:6379> pfmerge user2 user
OK
# 查看merge后的user2
127.0.0.1:6379> pfcount user2
(integer) 5
使用 Java 代码操作
import org.springframework.data.redis.core.HyperLogLogOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class RedisService {@Resourceprivate RedisTemplate<String, String> redisTemplate;/*** 记录用户访问** @param user*/public long statistic(String Key, String user) {HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();return hyperLogLog.add(Key, user);}/*** 统计当前 UV** @return*/public long size(String Key) {HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();return hyperLogLog.size(Key);}/*** 删除当前 key*/public void clear(String Key) {HyperLogLogOperations<String, String> hyperLogLog = redisTemplate.opsForHyperLogLog();hyperLogLog.delete(Key);}
}
HyperLogLog 实现原理及特点
- 原理:其实这是个概率问题。举个 Java 的例子,我们每次将一个字符串放入
HyperLogLog
,其实是把字符串转换成了一个值,可以把它当成hash
值,将这个值转换成 2 进制,从后向前看第一个 1 出现的位置。那么 1 出现在第三个位置的时候(xxxx x100
),概率是多少呢?(1/2)^3=1/8
,也就是大概有八个数字进到这个数据结构时,第一个 1 曾出现在第三个的位置的可能会比较大,所以我们只需要维护一个 1 出现位置的最大值(暂且称之为max position
),我们就可以知道整个HyperLogLog
数量是多少了。 - 去重:我们上面讲到
hash
值,其实整个算法就是将一个固定的value
固定的映射成一个数字就可以解决重复的问题了。如zhangsan
对应8
,那么max position=4
,再来一个zhangsan
,还是对应8
,则max position
不变。 - 特点:因为是概率问题,总会出现不准确的情况,所以你在使用
HyperLogLog
时,可以将user
数量设置大一些,如 100W。但是其结果,有可能你看到的是不到 100W,也有可能计算出来的 UV 还比 100W 大。
使用 Java 实现 HyperLogLog
public class HyperLogLogSelf {static class BitKeeper {private int maxBits;public void random() {// 这里的随机数可以当成一个对象的hashCode。long value = new Object().hashCode() ^ (2 << 32);long value = ThreadLocalRandom.current().nextLong(2L << 32);int bits = lowZeros(value);if (bits > this.maxBits) {this.maxBits = bits;}}/*** 低位有多少个连续0* 思路上 ≈ 倒数第一个1的位置** @param value* @return*/private int lowZeros(long value) {int i = 1;for (; i < 32; i++) {if (value >> i << i != value) {break;}}return i - 1;}}static class Experiment {private int n;private BitKeeper keeper;public Experiment(int n) {this.n = n;this.keeper = new BitKeeper();}public void work() {for (int i = 0; i < n; i++) {this.keeper.random();}}public void debug() {double v = Math.log(this.n) / Math.log(2);System.out.printf("%d %.2f %d\n", this.n, v, this.keeper.maxBits);}}public static void main(String[] args) {for (int i = 10000; i < 1000000; i += 10000) {Experiment exp = new Experiment(i);exp.work();exp.debug();}}
}
如上述代码所示,如果只有一个BitKeeper
,那么精度很难控制,BitKeeper
越多,则越精确,所以 Redis 在设置HyperLogLog
的时候,设置了16384
个桶,也就是2^14
,每个桶的maxbits
需要 6 个bit
来存储,最大可以表示maxbits=63
,于是总共占用内存就是2^14 * 6 / 8 = 12k
字节。
小结
我们从应用场景开始,讲述了HyperLogLog
的使用方法和实现原理,还给出了HyperLogLog
的 Java 简单实现。
最后,我们在使用HyperLogLog
的时候,需要注意:
HyperLogLog
需要占用12k
内存的(数据量大的时候),所以HyperLogLog
不适合单独存储一个user
相关的信息;HyperLogLog
是有一定精度损失的,可能比真实数量多,也可能比真实数量少,但基本上都在n‰(0<n<10)
以内。
使用 Redis 统计网站 UV 的方法相关推荐
- redis服务器信息统计,利用Redis统计网站在线活跃用户的方法
前言 在工作中我们经常遇到这样的需求,要对某个在线网站的活跃用户数量进行统计.这里我们以redis为例,说明一下其实现的过程. 实现方法 在Redis中存在bitmap这种数据类型,这种数据类型是建立 ...
- mysql和redis统计网站活跃度,最代码网站用户私信列表采用mysql union查询优化为Redis查询的经验和相关代码片段分享...
由于用户和私信的数据量逐渐增加,查询用户和其他用户的私信合并排重排序的sql语法给mysql带来了很大的压力,springdata jpa的hql查询语法如下:select id from (sele ...
- 拼多多面试:如何用 Redis 统计独立用户访问量?
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:沙茶敏碎碎念 来源:https://url.cn/5tQPE ...
- 拼多多面试|如何用 Redis 统计独立用户访问量?
众所周至,拼多多的待遇也是高的可怕,在挖人方面也是不遗余力,对于一些工作3年的开发,稍微优秀一点的,都给到30K的Offer 当然,拼多多加班也是出名的,一周上6天班是常态,每天工作时间基本都是超过1 ...
- 拼多多面试真题:如何用 Redis 统计独立用户访问量!
作者 | 沙茶敏碎碎念 来源 | http://toutiao.com/i6695734985246114312/ 众所周至,拼多多的待遇也是高的可怕,在挖人方面也是不遗余力,对于一些工作3年的开发, ...
- 拼多多的真实面试题:数亿的用户,如何用Redis统计独立用户访问量
众所周至,拼多多的待遇也是高的可怕,在挖人方面也是不遗余力,对于一些工作3年的开发,稍微优秀一点的,都给到30K的Offer,当然,拼多多加班也是出名的,一周上6天班是常态,每天工作时间基本都是超过1 ...
- discuz设置用户每天回帖数_如何用Redis统计独立用户访问量,除了Hash跟Bitset,还有这个...
众所周至,拼多多的待遇也是高的可怕,在挖人方面也是不遗余力,对于一些工作3年的开发,稍微优秀一点的,都给到30K的Offer,当然,拼多多加班也是出名的,一周上6天班是常态,每天工作时间基本都是超过1 ...
- Flume+Kafka+Storm+Redis构建大数据实时处理系统:实时统计网站PV、UV+展示
http://blog.51cto.com/xpleaf/2104160?cid=704690 1 大数据处理的常用方法 前面在我的另一篇文章中<大数据采集.清洗.处理:使用MapReduce进 ...
- 用户行为分析大数据系统(实时统计每个分类被点击的次数,实时计算商品销售额,统计网站PV、UV )
Spark Streaming实战对论坛网站动态行为pv,uv,注册人数,跳出率的多维度分析_小强签名设计 的博客-CSDN博客_spark streaming uv 实时统计每天pv,uv的spar ...
最新文章
- 李飞飞力赞论文:描述视频密集事件新模型 !(附论文)
- 计算机视觉成安防“显学”落地仍需解决这些难题
- 如何让本机时间与局域网的一台电脑的日期同步?
- yum 下载软件的存放位置
- Ceph分布式存储实战2.4 本章小结
- 一步步编写操作系统4 安装x86虚拟机 bochs
- 1022词法分析实验总结
- IoC、Spring 环境搭建、Spring 创建对象的三种方式、DI
- PHP 7 的五大新特性
- 核心金融场景分布式事务
- MySQL下载安装新手教程
- 一步步学习java后台(一)(IDEA, Spring, Maven, MyBatis)
- Excel查询A列中的数据是否在B中存在
- vue项目中Echarts两个图表之间连接两条线
- openGL增强表面细节--凹凸贴图具体实现
- 关于风险管理,如何将思维从项目升维到项目群?
- nginx小知识: 通过location下 root,alias配置转发图片目录
- refind引导的win10+ubuntu18.04开机启动
- 【Python之GUI开发案例】用Python的tkinter开发聚合翻译神器
- Android Bluetooth蓝牙开发:发现Bluetooth蓝牙设备(1)