使用 Redis 进行阅读数统计并定时持久化
之前,统计每篇博文的阅读数的方式是经过筛选去重之后直接更新数据库,并发压力直接传导到数据库,假设1秒有1000个并发请求,传统方案会在1秒内并发进行1000次数据库更新操作。
为了降低数据库的并发压力,需要重新设计统计服务。思路是即使1秒有1万个并发请求,也只是依次更新数据库,对数据库没有并发压力。
统计服务要做的事情很专一:去重+计数
去重的业务根据具体的需要来设计规则,例如一个用户1个小时内所有访问都只计算一次,没有用户信息的按 IP 地址或者浏览器标识去统计。去重就是把这些标志去重,有多种实现方法,Hash
过滤,数据库唯一性等。
这里我们采用 Redis 的 HyperLogLog
,简称HLL
,它是一个高效的结构,内存占用极小,能快速统计出所有不一样的元素。有三个方法:
PFADD
:向结构中增加一个元素,其实 HLL
并没有存储这个元素,而是按照概率论的算法进行统计,所以 12K 内存就能统计 2^64 个数据,返回值为1表示该元素被统计了,反之则没有;
PFMERGE
:可以把合并两个 HLL
;
PFCOUNT
:获取统计数,这个算法虽然高效,但是也有弊端,就是存在误差,在 1% 以下,只要不是非常精确的业务基本上也是可以忽略的。
我们的业务逻辑实现比较简单,可以用博文和时间作Key
,hll_{postId}_{yyyymmddhh}
,再把访问博文的用户标志按照规则生成一个字符串,name_{userName}
ip_{ipAddress}
,用PFADD
它添加进去,HLL
会判断是否重复,重复的就不会统计,然后把不重复的也就是返回值为 1 的 Key 存储到集合 SET
中,记录下来方便遍历。
经过去重之后我们就要统计总数并持久化到数据库中,每篇博文在 Redis
中对应至少一个 HLL
结构,创建观察者服务不停地Pop
SET
中所有的 hll 的 Key
,然后再通过PFCOUNT
得到对应的博文的统计数。 拿到统计数之后再发送给持久化服务处理,或者通过负载均衡交给多个持久化服务处理。
如上图所示,部署多个 Counter web服务负责接收请求,一个 redis 服务或者集群负责统计阅读数,多个 watcher 服务负责把统计结果取出来,交给多个数据存储服务去持久化。
我们线上用的是 docker-swarm 集群,它本身就有负载均衡作用,所以可以省略负载均衡。
这里之所以用SET.POP()
,是因为它支持并发访问的,不会锁 Redis。如果直接遍历所有 HLL Key
,就只能用 SCAN
全局查找,虽然也不会锁住 Redis,但是它不支持并行操作,对扩展不够友好。
这样架构的优点就是可以横向扩展,任何地方出现性能瓶颈都能通过扩展解决。
参考资料
多个消费者重复消费问题
HypterLogLog
HyperLogLog 原理
使用 Redis 进行阅读数统计并定时持久化的更多相关文章
- Django訪问量和页面PV数统计
http://blog.csdn.net/pipisorry/article/details/47396311 以下是在模板中做一个简单的页面PV数统计.model阅读量统计.用户訪问量统计的方法 简 ...
- 利用Github Pages创建的Jekyll模板个人博客添加阅读量统计功能
目录 前言 准备工作 模板文件修改 写在最后 内容转载自我自己的博客 @(文章目录) 前言 Jekyll 是一个简单的免费的 Blog 生成工具,类似 WordPress .它只是一个生成静态网页的工 ...
- Elasticsearch索引增量统计及定时邮件实现
0.需求 随着ELKStack在应用系统中的数据规模的急剧增长,每天千万级别数据量(存储大小:10000000*10k/1024/1024=95.37GB,假设单条数据10kB,实际远大于10KB)的 ...
- php后台的在控制器中就可以实现阅读数增加
$smodel=M('Sswz');$smodel->where($map)->setInc('view' ,1);php后台的在控制器中就可以实现阅读数增加前台不需要传值
- C语言 · 单词数统计
单词数统计 输入一个字符串,求它包含多少个单词. 单词间以一个或者多个空格分开. 第一个单词前,最后一个单词后也可能有0到多个空格. 比如:" abc xyz" 包含两个单词 ...
- java代码行数统计工具类
package com.syl.demo.test; import java.io.*; /** * java代码行数统计工具类 * Created by 孙义朗 on 2017/11/17 0017 ...
- Qt编写自定义控件69-代码行数统计
一.前言 代码行数统计主要用来统计项目中的所有文件的代码行数,其中包括空行.注释行.代码行,可以指定过滤拓展名,比如只想统计.cpp的文件,也可以指定文件或者指定目录进行统计.写完这个工具第一件事情就 ...
- Spark学习笔记1——第一个Spark程序:单词数统计
Spark学习笔记1--第一个Spark程序:单词数统计 笔记摘抄自 [美] Holden Karau 等著的<Spark快速大数据分析> 添加依赖 通过 Maven 添加 Spark-c ...
- Java作业 题目:16版.真实员工数统计
题目:16版.真实员工数统计 该资源支持按部自动给分,评分规则如下: sjkdfhslkfdhdsiog函数定义测试 sjkdfhslkfdhdsiog函数定义测试 sjkdfhslkfdhdsiog ...
随机推荐
- CentOS 7 Keepalive 脚本不执行解决
目录 问题 问题一 括号问题 问题二 脚本名称问题 问题 起因是在测试部署 Altls + Keepalive 高可用读写分离,测试停止Atlas服务的时候,发现Keepalive不会自动主从切换,就 ...
- mysql 插入string类型变量时候,需要注意的问题,妈的,害我想了好几个小时!!
很多人在用php+MySQL做网站往数据库插入数据时发现如下错误: 注册失败!Unknown column '1a' in 'field list' 结果发现用数字提交是没有问题的,其他如char型就 ...
- Linux集群介绍、keepalived介绍及配置高可用集群
7月3日任务 18.1 集群介绍18.2 keepalived介绍18.3/18.4/18.5 用keepalived配置高可用集群扩展heartbeat和keepalived比较http://blo ...
- RestSharp Simple REST and HTTP API Client for .NET
var client = new RestClient("http://example.com"); // client.Authenticator = new HttpBasic ...
- Date、Calendar和GregorianCalendar的使用
java.util 包提供了 Date 类来封装当前的日期和时间. Date 类提供两个构造函数来实例化 Date 对象. 第一个构造函数使用当前日期和时间来初始化对象. Date public st ...
- Too many open files的四种解决办法
[摘要] Too many open files有四种可能:一 单个进程打开文件句柄数过多,二 操作系统打开的文件句柄数过多,三 systemd对该进程进行了限制,四 inotify达到上限. 领导见 ...
- KubeSphere 日志备份与恢复实践
为什么需要日志备份 KubeSphere 日志系统使用 Fluent Bit + ElasticSearch 的日志采集存储方案,并通过 Curator 实现对 Index 的生命周期管理,定期清理久 ...
- 压缩感知重构算法之SP算法python实现
压缩感知重构算法之OMP算法python实现 压缩感知重构算法之CoSaMP算法python实现 压缩感知重构算法之SP算法python实现 压缩感知重构算法之IHT算法python实现 压缩感知重构 ...
- Spring整合Shiro 权限 角色 用户关系分析
Spring整合Shiro 权限 角色 用户关系分析 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 前置内容 之前我们学习了,使用注解的方式去完成权限的控制,当然,也是静态的,也就 ...
- 【Oracle】ORA-12560: TNS: 协议适配器错误
问题现象: ORA-12560: TNS: 协议适配器错误 解决方法: 启动监听服务