Java统计网站PV、UV

Java统计网站PV、UV

前言当一个系统上线后,基本都需要统计用户活跃度,活跃度一般有两个指标,一个是PV(Page View)页面浏览量,一个是UV(Unique Visitor)唯一用户量,比如微信小程序后台中就有每小时UV的统计。

什么是PV,UVPV(Page View)页面浏览量,当页面被加载刷新一次,PV就会记录一次,一般PV越高,UV也会越高;但如果网站被爬虫或者被疯狂刷新,PV就会非常高。UV(Unique Visitor)独立用户量,一天当中访问网站的用户数,不管是上午访问还是下午访问,一个用户都只记录一次。比如你在上午访问了腾讯社区2次,下午访问了腾讯社区3次,那么PV就是2 + 3 = 5次,UV为1次。

为什么需要统计PV,UV分析知道哪些页面是用户经常访问的,缓存常用数据,针对性的提升某些接口效率。如果某些页面访问量远远高于其他页面,我们还可以单独部署一台服务器给这些高访问页面使用。监控系统,防止被爬虫或盗刷。预计为广告主投放广告带来的流量。核心讲解PV统计相对简单,使用Redis,以日期为key,value为每天的访问量,用户每访问一次value就+1,统计PV时,读取PV值即可。UV统计,同样日期为key,value为唯一标识用户的ID或IP的Set集合(本文使用用户IP来作为唯一标识),用户访问时如果Set中不存在当前访问用户IP,则UV+1,并将IP加入Set中;当我们读取UV时,即读取Set中元素个数。

如果不想在Redis中保存太多数据,我们可以把每天的PV、UV数据落库一次。

功能实现这里使用RedisTemplate访问redis,使用Hutool的ServletUtil获取用户ip。

🎉INCR命令统计PV,INCR key,将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。代码语言:java复制@Resource

private RedisTemplate redisTemplate;

//redis的pv和uv前缀

final static String PV\_PREFIX = "pv\_";

final static String UV\_PREFIX = "uv\_";

/\*\*

\* 统计pv,uv

\* @return 返回统计后的pv,uv值

\*/

@GetMapping()

public AjaxResult statist(HttpServletRequest request) {

// 获取yyyy-MM-dd格式的日期

Date date = new Date();

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

String today = sdf.format(date);

String pvKey = PV\_PREFIX + today;

String uvKey = UV\_PREFIX + today;

// pv + 1, incr命令:将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

Long pvNum = redisTemplate.opsForValue().increment(pvKey);

// hutool获取用户ip

String clientIP = ServletUtil.getClientIP(request, null);

// 将ip放到redis的set中

redisTemplate.opsForSet().add(uvKey, clientIP); //"SADD myset hello world"

Long uvNum = redisTemplate.opsForSet().size(uvKey); // "SCARD myset"

AjaxResult ans = AjaxResult.success();

ans.put("pv",pvNum);

ans.put("uv",uvNum);

return ans;

}HyperLogLog统计UV为什么使用HyperLogLog在统计UV时我们刚刚使用的是Set保存全部的IP,它本身是去重的,最终Set元素的个数就是我们需要的值,用户量不多时还是可以接受的,但当用户人数上去时,达到百万,千万级时,保存全部ip还是非常占内存的。

🎉SADD命令统计UV,SADD key value1 value2,将value1和value2添加进set中。SCARD key获取key的长度。那有没有什么办法能够减少内存使用?Redis有提供HyperLogLog的算法,它是根据统计学的基数估算算法,用最多12k的内存空间进行基数统计,但由于它是估算的算法,会有一定的误差,误差率约为0.81%。

HyperLogLog的UV统计关于HyperLogLog的命令我们主要使用以下三个:

PFADD key value1 value2: 用于数据添加,可以一次性添加多个。添加的重复记录会去重,和Set一样。PFCOUNT key: 对 key 进行统计计数。PFMERGE destkey sourcekey1 sourcekey2: 合并多个统计结果,在合并的过程中,会自动去重多个集合中重复的元素。Redis使用HyperLogLog统计UV:

代码语言:java复制// 将ip放到redis的HyperLogLog中

redisTemplate.opsForHyperLogLog().add(uvKey,clientIP); //PFADD mypf ip1 ip2

Long uvNum = redisTemplate.opsForHyperLogLog().size(uvKey); //PFCOUNT mypfHyperLogLog的误差率我们实际体验下,在Set和HyperLogLog中都放入10w条数据,比较他们的误差率。

代码语言:java复制@Resource

private RedisTemplate redisTemplate;

String uvSetKey = "uv_set_2024-07-13";

String uvPFKey = "uv_pf_2024-07-13";

long num = 100000;

@Test

public void initData(){

// 初始化添加100w条数据

for (int i = 1; i <= num; i++) {

redisTemplate.opsForSet().add(uvSetKey,i);

redisTemplate.opsForHyperLogLog().add(uvPFKey,i);

}

}

@Test

public void getData(){

// 获取误差率

Long setSize = redisTemplate.opsForSet().size(uvSetKey);

Long pfSize = redisTemplate.opsForHyperLogLog().size(uvPFKey);

DecimalFormat format = new DecimalFormat("##.00%");

String setFormat = format.format((double) setSize / (double) num);

String pfFormat = format.format((double) pfSize / (double) num);

System.out.println("set: " + setFormat);

System.out.println("pf: " + pfFormat);

}结果输出:

代码语言:sql复制set: 100.00%

pf: 99.56%可以看到Set的是完全没有误差的,本次HyperLogLog的误差率为0.44%,对于统计UV这种数据时,我们一般都是有一定容忍度的,我们更专注服务器的资源使用情况,0.81%左右的误差我们是可以接受的。

HyperLogLog的内存使用在Navicat中我们可以看到10w条数据的set占用内存为4M,而HyperLogLog只占用了12k。

此外,我们可以通过Redis的命令debug object key查看某个key的序列化后的长度。返回的参数有以下五个:

➢ Value at :key 的内存地址

➢ refcount :引用次数

➢ encoding :编码类型

➢ serializedlength:序列化长度(单位是 Bytes)

➢ lru_seconds_idle:空闲时间

返回的serializedlength 仅仅代表 key 序列化后的长度,key 在内存中实际占用的内存会比这个值大。不过,它也侧面反应了一个 key 所占用的内存。

演示完整代码前端(vue3):https://gitee.com/HT3902LY/writing_front

后端(Java):https://gitee.com/HT3902LY/writing_back

相关推荐

钽电容耐压的标识方法
365娱乐平台网址

钽电容耐压的标识方法

📅 09-30 👁️ 7562
亚洲球队在世界杯上的十佳成绩,第一名争议最多
历届世界杯全场0-0 历届世界杯战绩
365足球提现

历届世界杯全场0-0 历届世界杯战绩

📅 12-05 👁️ 7240