使用 Redis 统计网站 UV 的方法
使用 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 统计网站 UV 的方法(概率算法) 文章目录 前言思路HyperLogLog 使用 Redis 命令操作使用 Java 代码操作 HyperLogLog 实现原理及特点使用 Java 实现 HyperLogLog小结 前言 网站 UV 就是指网站的独立用户访问量Unique Visitor,即相同用户的多次访问需要…...
黑客工具软件大全
黑客工具软件大全100套 给大家准备了全套网络安全梓料,有web安全,还有渗透测试等等内容,还包含电子书、面试题、pdf文档、视频以及相关的网络安全笔记 👇👇👇 《黑客&网络安全入门&进阶学习包》 &a…...
uniapp主题切换功能的第二种实现方式(scss变量+require)
在上一篇 “uniapp主题切换功能的第一种实现方式(scss变量vuex)” 中介绍了第一种如何切换主题,但我们总结出一些不好的地方,例如扩展性不强,维护起来也困难等等,那么接下我再给大家介绍另外一种切换主题的…...
# 蓝牙音频相关知识
蓝牙音频相关知识 文章目录 蓝牙音频相关知识1 音频源2 蓝牙音频编解码器3 一些标准4 蓝牙音频其他相关知识4.1 蓝牙版本4.2 ANC(主动降噪)4.3 音响相关参数4.4 音质评价4.5 HI-Fi声音特点4.6 耳机线材4.7 耳机分类4.8 IP防尘防水等级4.9 噪音与量化噪音…...
【AI作画】使用DiffusionBee with stable-diffusion在mac M1平台玩AI作画
DiffusionBee是一个完全免费、离线的工具。它简洁易用,你只需输入一些标签或文本描述,它就能生成艺术图像。 DiffusionBee下载地址 运行DiffusionBee的硬性要求:MacOS系统版本必须在12.3及以上 DBe安装完成后,去C站挑选自己喜欢…...
2 STM32库函数 之 通用同步异步收发器(USART、串口)所有函数的介绍及使用
2 STM32库函数 之 通用同步异步收发器(USART、串口)所有函数的介绍及使用 前言一、USART固件库函数预览二、USART固件库函数具体介绍2.1 库函数 USART_DeInit2.2 库函数 USART_Init2.2.1 USART_InitTypeDef structure2.2.2 USART_InitTypeDef 成员 USART…...
SpringCloudAlibaba整合Sentinel实现流量控制熔断降级
目录 一、概念 二、整合Sentinel控制台 三、Sentinel规则配置 四、@SentinelResource资源保护注解...
CentOS 7安装 Postgre
零、前置条件 系统CentOS 7,并已联网,已安装gcc或者g编译器,GNU make版本3.80或以上,系统有至少一个除root之外的普通用户user gcc安装-参考链接查看make命令的版本——make --version更新make版本-参考链接postgresql的使用一般…...
rpc 异步非阻塞 io 配置 线程池和队列
相关 雪崩 - 如何重试 - sla和重试风暴的双保证_个人渣记录仅为自己搜索用的博客-CSDN博客 接口耗时公式 耗时 cpu时间 io时间 线程池数量 最佳数目 1s / 平均cpu时间 * 内核数. 最大平均cpu时间 接口耗时- all外部io时间. 结合gc , linux本身其他线程, 只会还少点. …...
【Turfjs的java版本JTS】前面讲了Turfjs可以实现几何计算,空间计算的功能,如果后端要做这项功能也有类似的类库,JTS
JTS Java Topology Suite 几何计算: 1. 前端js就用这个 Turfjs的类库。参考网站: 计算两线段相交点 | Turf.js中文网 2. 后端java语言就可以用 JTS这个类库,参考网站: JTS参考网站: 1. https://github.com/locatio…...
从Window中先多瞥几眼
JavaFx17官方文档中有如下的描述: Window类是一个顶层窗口类,在其中可以承载场景,并与用户交互。窗口可以是Stage、PopupWindow或其他类似的顶层窗口。 JavaFX Stage类是顶级的JavaFX容器。初级阶段由平台搭建。其他Stage对象可以由应用程序构造。 许多Stage属性是只读的…...
【STM32训练—WiFi模块】第二篇、STM32驱动ESP8266WiFi模块获取天气
目录 第一部分、前言 1、获取心知天气API接口 2、硬件准备 第二部分、电脑串口助手调试WIFI模块获取天气 1、ESP8266获取天气的流程 2、具体步骤 第三部分、STM32驱动ESP8266模块获取天气数据 1、天气数据的解析 1.1、什么函数来解析天气数据? 2.1、解析后…...
Maven私服
Maven 私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,用来代理位于外部的远程仓库(中央仓库、其他远程公共仓库)。 建立了 Maven 私服后,当局域网内的用户需要某个构件时,会按照如下顺序进行请求…...
手写RPC总结篇
协议制定:client到server做交互的通信协议,比如request response 网络端点peer 难点1 : Jetty嵌入 ◆jetty Server ◆ServletContextHandler ◆ServletHolder jetty server 起到网络监听的作用ServletContextHandler注册到jetty server中ServletHolde…...
c++11 标准模板(STL)(std::ios_base)成员类型与常量
流打开模式类型 std::ios_base::openmode typedef /*implementation defined*/ openmode; static constexpr openmode app /*implementation defined*/ static constexpr openmode binary /*implementation defined*/ static constexpr openmode in /*implementation defi…...
我用 ChatGPT 写 2023 高考语文作文:全国卷(一)
【2023】新高考|卷 “好的故事,可以帮我们更好地表达和沟通,可以触动心灵、启迪智慧:好的故事以改变一个人的命运,可以展现一个民族的形象故事是有力量的。” 以上材料引发了你怎样的联想和思考?请写一篇文章 要求&…...
4.java转义符,javadoc 标签
java常用转义字符 在控制台,输入tab键,可以实现命令补全 (如何解决cmd中Tab键不能自动补充的问题?百度一下) \t : 一个制表符,实现对齐功能\n : 换行符\ \ : 一个\\ " :一个"\ ’ : 一个’\r : 一个回车 …...
PinYin4j库的使用
一、PinYin4j库简介 1、PinYin4j简介 Pinyin4j 是一个流行的 Java 库,支持汉字和大多数流行的拼音系统之间的转换(汉语拼音,罗马拼音等)。可自定义拼音输出格式,功能强大。 官网地址:http://pinyin4j.sou…...
日志框架 --- Logback
文章目录 1. 什么是logback2. logback的日志级别3. 日志级别的层级4. logback配置文件4.1 logger标签4.2 root标签4.3 appender标签4.4 filter标签4.5 encoder标签 5. 整体演示5.1 配置文件5.2 运行结果 1. 什么是logback Logback是一个用于Java应用程序的日志框架,…...
QML 与 Python 交互
在 Qt 中,C 和 QML 交互一般有如下三种方法 上下文属性:setContextProperty( )向引擎注册类型:调用 qmlRegisterType( )QML 扩展插件:虽然有很大的灵活性,但是用 Python 创建 QML 插件比较麻烦,所以这种方法…...
深入GORM源码:手把手教你为自定义字段打造专属‘Clause钩子’
深入GORM源码:手把手教你为自定义字段打造专属‘Clause钩子’ 在当今快速迭代的业务场景中,数据库操作早已不再是简单的CRUD。当我们面对复杂的状态流转、多租户隔离或敏感数据加密时,往往需要在数据持久化层植入特定的业务逻辑。GORM作为Go生…...
深度理解 C++ 继承与多态:从底层原理到实战技巧
目录 一、 继承:不仅是代码的复用 1.1 三种继承方式的差异 1.2 构造与析构的顺序(避坑指南) 二、 多态:让程序具备“生命力” 2.1 虚函数(Virtual Function) 2.2 核心代码示例 三、 深度思考&#x…...
半导体制造模式之争:IDM与Fabless的战略选择与未来趋势
1. 半导体制造模式的世纪之争:IDM与Fabless的路线抉择“真男人就该有自己的晶圆厂。” 这句话出自AMD创始人杰里桑德斯之口,在半导体产业的早期,它像一句战斗口号,定义了那个时代顶级芯片公司的雄心——将设计与制造牢牢掌握在自己…...
生物 -- 神经系统(三)
1、髓鞘髓鞘是包裹在神经细胞轴突外层的绝缘膜,主要由脂质和蛋白质构成,起到加速神经信号传导、绝缘防漏电以及保护和修复神经的作用。你可以把它想象成电线外的绝缘皮,确保电流(即神经信号)高效、准确地传输。核心功…...
缠论可视化插件:3个步骤让你的技术分析不再迷茫
缠论可视化插件:3个步骤让你的技术分析不再迷茫 【免费下载链接】Indicator 通达信缠论可视化分析插件 项目地址: https://gitcode.com/gh_mirrors/ind/Indicator 你是否曾经面对复杂的K线图感到无从下手?缠论作为技术分析的重要理论,…...
ClawSpark:一键部署私有AI智能体,实现本地化智能助手
1. 项目概述:ClawSpark,一键部署的私有AI智能体如果你和我一样,对AI智能体(Agent)的潜力感到兴奋,但又对将个人数据、工作流程乃至核心业务逻辑完全托付给云端API心存疑虑,那么ClawSpark的出现&…...
开源协作平台Penny:为女性开发者打造包容性技术社区
1. 项目概述:一个为女性开发者量身定制的开源协作平台最近在GitHub上闲逛,发现了一个挺有意思的项目,叫“WomenBuilt/penny”。光看这个名字,你可能会有点摸不着头脑,这“penny”是啥?一个记账应用…...
淘宝淘金币自动脚本:每天15分钟解放双手的终极指南
淘宝淘金币自动脚本:每天15分钟解放双手的终极指南 【免费下载链接】taojinbi 淘宝淘金币自动执行脚本,包含蚂蚁森林收取能量,芭芭农场全任务,解放你的双手 项目地址: https://gitcode.com/gh_mirrors/ta/taojinbi 淘宝淘金…...
Lie群方法在机器人状态估计中的创新应用
1. 状态估计技术演进与Lie群方法的核心价值在机器人导航与定位领域,状态估计技术扮演着大脑的角色。想象一下,当你在陌生城市使用手机导航时,系统需要实时融合GPS、陀螺仪和加速度计的数据来确定你的位置——这正是状态估计的典型应用场景。传…...
人机冲突类型学:基于意义行为原生论与自感痕迹论的系统性分析
人机冲突类型学:基于意义行为原生论与自感痕迹论的系统性分析 摘要:本文旨在构建一种新的人机冲突类型学,其理论基础是岐金兰的“意义行为原生论”与“自感痕迹论”。不同于现有研究从外部功能或伦理原则出发分类冲突,本文提出&am…...
