使用 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 ...
随机推荐
- 西瓜哥:公有云也“All-Flash”?
本文转载自 高端存储知识 Gartner在2018年Market Insight: Preparing for the SSD Rise and HDD Demise一文中指出:当闪存介质降到HDD每 ...
- 英飞凌TC297 PSPR与DSPR
TC297的系统架构图如下,3个核CPU0/1/2有各自的RAM,分为程序RAM和数据RAM,即PSPR和DSPR.PSPR(或者叫做PRAM)主要用于放置静态函数,提高函数执行效率,或放置flash ...
- linux中RabbitMQ安装教程
linux中RabbitMQ安装教程 在做一个微服务项目时候用到消息队列,于是深入了解了消息队列知识,并在linux上安装了Rabbitmq,本博客介绍Rabbitmq的安装教程,想要深入了解消息队列 ...
- luogu P1754 球迷购票问题
题目背景 盛况空前的足球赛即将举行.球赛门票售票处排起了球迷购票长龙. 按售票处规定,每位购票者限购一张门票,且每张票售价为50元.在排成长龙的球迷中有N个人手持面值50元的钱币,另有N个人手持面值1 ...
- [TimLinux] TCP全连接队列满
0. TCP三次握手 该图来自:TCP SOCKET中backlog参数的用途是什么? syns queue: 半连接队列 accept queue: 全连接队列 控制参数存放在文件:/proc/sy ...
- [TimLinux] JavaScript 引用类型——Date
1. Date var now = new Date(); // 不传参数,获取当前日期.时间. now.getDay(); // 日期 now.getMonth(); // 月份 now.getFu ...
- HDU5343 MZL's Circle Zhou(SAM+记忆化搜索)
Problem Description MZL's Circle Zhou is good at solving some counting problems. One day, he comes u ...
- 最全的linux基础命令
第1章 linux命令 1.1 线上查询及帮助命令 help命令*** help前面接你要查询的命令:例如ls [root@server02 ~]# ls --help 用法:ls [选项]... [ ...
- Asp.net Core 异常日志与API返回值处理
需求: 1.对异常进行捕获记录日志 并且修改返回值给前端 解释: ILogger4是自定义的一个日志,更改它就好 解决方案1: 使用中间件进行异常捕获并且修改其返回值 public class Err ...
- 笔记||Python3之函数
函数: 函数的概念:就是一段代码:一段操作流程. 优点:代码量少.简洁. 维护起来方便 -- 在函数的定义进行修改 函数的定义:1 - def 函数名(): 函数内容 2 - 函 ...