网站运营数据
我们知道对于一个网站而言运营数据很重要,他直接关系到你可以拉到多少投资,以及网站以后的优化方向。
常见的运营指标有UV(Qnique Visitor,独立访客),PV(Page View,页面浏览量), DAU(Daily Active User,日活用户量),MAU(Monthly Active User 月活用户量).
一般UV是如何统计的呢?主要通过请求ip来计算,一个独立ip算一个访客,需要去重统计。
该如何存储呢?mysql?这个可以直接忽略了,很难扛住亿级流量。
redis的set类型呢?ip类型可以转成8字节的long型数字,存储一亿个需要762MB内存,貌似可以接受,但是要知道一个网站不止一个网页,要统计所有网页的UV的话就无法接受了。
bitmap类型呢?亿级访问需要12MB内存,1000个网页12G内存,还算可以,但不是最优方案。接下来就是我们的主角HyperLogLog登场了。
HyperLogLog类型
HyperLogLog的优点是可以用来做基数统计,所谓基数就是会去重。而且它只需要花费12KB的内存,就可以计算接近2^64个不同的基数,缺点是会有0.81%的误差,不像bitmap是精确统计,但节省的内存和运行速度不是一个数量级的,PS: bitmap的bitcount命令的时间复杂度是O(N),也就是说数据越多速度越慢。亿级流量大概几十万的误差,可以接受,综合考虑下来无疑是最优方案。
HyperLogLog原理很复杂,使用起来却超级简单,常用的就两个命令
添加元素:
pfadd key element
统计元素个数:
pfcount key
理论加实践,我们直接上代码:
public class HyperLogLogController extends BaseController {
@Autowired
private RedisTemplate redisTemplate;
@PostMapping
@ApiOperation(value = "模拟访问")
public Result visit() {
for (int i = 1; i <= 200; i++) {
String ip = RandomUtils.nextInt(256) + "." + RandomUtils.nextInt(256) + "." + RandomUtils.nextInt(256) + "." + RandomUtils.nextInt(256);
//放入ip
redisTemplate.opsForHyperLogLog().add("UV", ip);
}
return resultOk();
}
@GetMapping
@ApiOperation(value = "统计UV")
public Result total() {
long total = redisTemplate.opsForHyperLogLog().size("UV");
return resultOk(total);
}
}
对于HyperLogLog原理感兴趣的可以参考这篇文章: https://juejin.cn/post/6844903785744056333
参考项目(模块: SpringBoot-HelloWorld): https://gitee.com/huatin/java-test