之前,统计每篇博文的阅读数的方式是经过筛选去重之后直接更新数据库,并发压力直接传导到数据库,假设1秒有1000个并发请求,传统方案会在1秒内并发进行1000次数据库更新操作。

为了降低数据库的并发压力,需要重新设计统计服务。思路是即使1秒有1万个并发请求,也只是依次更新数据库,对数据库没有并发压力。

统计服务要做的事情很专一:去重+计数

去重的业务根据具体的需要来设计规则,例如一个用户1个小时内所有访问都只计算一次,没有用户信息的按 IP 地址或者浏览器标识去统计。去重就是把这些标志去重,有多种实现方法,Hash过滤,数据库唯一性等。

这里我们采用 Redis 的 HyperLogLog,简称HLL,它是一个高效的结构,内存占用极小,能快速统计出所有不一样的元素。有三个方法:
PFADD:向结构中增加一个元素,其实 HLL 并没有存储这个元素,而是按照概率论的算法进行统计,所以 12K 内存就能统计 2^64 个数据,返回值为1表示该元素被统计了,反之则没有;
PFMERGE:可以把合并两个 HLL
PFCOUNT:获取统计数,这个算法虽然高效,但是也有弊端,就是存在误差,在 1% 以下,只要不是非常精确的业务基本上也是可以忽略的。

我们的业务逻辑实现比较简单,可以用博文和时间作Keyhll_{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 进行阅读数统计并定时持久化的更多相关文章

  1. Django訪问量和页面PV数统计

    http://blog.csdn.net/pipisorry/article/details/47396311 以下是在模板中做一个简单的页面PV数统计.model阅读量统计.用户訪问量统计的方法 简 ...

  2. 利用Github Pages创建的Jekyll模板个人博客添加阅读量统计功能

    目录 前言 准备工作 模板文件修改 写在最后 内容转载自我自己的博客 @(文章目录) 前言 Jekyll 是一个简单的免费的 Blog 生成工具,类似 WordPress .它只是一个生成静态网页的工 ...

  3. Elasticsearch索引增量统计及定时邮件实现

    0.需求 随着ELKStack在应用系统中的数据规模的急剧增长,每天千万级别数据量(存储大小:10000000*10k/1024/1024=95.37GB,假设单条数据10kB,实际远大于10KB)的 ...

  4. php后台的在控制器中就可以实现阅读数增加

    $smodel=M('Sswz');$smodel->where($map)->setInc('view' ,1);php后台的在控制器中就可以实现阅读数增加前台不需要传值

  5. C语言 · 单词数统计

    单词数统计 输入一个字符串,求它包含多少个单词. 单词间以一个或者多个空格分开. 第一个单词前,最后一个单词后也可能有0到多个空格. 比如:" abc    xyz" 包含两个单词 ...

  6. java代码行数统计工具类

    package com.syl.demo.test; import java.io.*; /** * java代码行数统计工具类 * Created by 孙义朗 on 2017/11/17 0017 ...

  7. Qt编写自定义控件69-代码行数统计

    一.前言 代码行数统计主要用来统计项目中的所有文件的代码行数,其中包括空行.注释行.代码行,可以指定过滤拓展名,比如只想统计.cpp的文件,也可以指定文件或者指定目录进行统计.写完这个工具第一件事情就 ...

  8. Spark学习笔记1——第一个Spark程序:单词数统计

    Spark学习笔记1--第一个Spark程序:单词数统计 笔记摘抄自 [美] Holden Karau 等著的<Spark快速大数据分析> 添加依赖 通过 Maven 添加 Spark-c ...

  9. Java作业 题目:16版.真实员工数统计

    题目:16版.真实员工数统计 该资源支持按部自动给分,评分规则如下: sjkdfhslkfdhdsiog函数定义测试 sjkdfhslkfdhdsiog函数定义测试 sjkdfhslkfdhdsiog ...

随机推荐

  1. 西瓜哥:公有云也“All-Flash”?

    本文转载自 高端存储知识 Gartner在2018年Market Insight: Preparing for the SSD Rise and HDD Demise一文中指出:当闪存介质降到HDD每 ...

  2. 英飞凌TC297 PSPR与DSPR

    TC297的系统架构图如下,3个核CPU0/1/2有各自的RAM,分为程序RAM和数据RAM,即PSPR和DSPR.PSPR(或者叫做PRAM)主要用于放置静态函数,提高函数执行效率,或放置flash ...

  3. linux中RabbitMQ安装教程

    linux中RabbitMQ安装教程 在做一个微服务项目时候用到消息队列,于是深入了解了消息队列知识,并在linux上安装了Rabbitmq,本博客介绍Rabbitmq的安装教程,想要深入了解消息队列 ...

  4. luogu P1754 球迷购票问题

    题目背景 盛况空前的足球赛即将举行.球赛门票售票处排起了球迷购票长龙. 按售票处规定,每位购票者限购一张门票,且每张票售价为50元.在排成长龙的球迷中有N个人手持面值50元的钱币,另有N个人手持面值1 ...

  5. [TimLinux] TCP全连接队列满

    0. TCP三次握手 该图来自:TCP SOCKET中backlog参数的用途是什么? syns queue: 半连接队列 accept queue: 全连接队列 控制参数存放在文件:/proc/sy ...

  6. [TimLinux] JavaScript 引用类型——Date

    1. Date var now = new Date(); // 不传参数,获取当前日期.时间. now.getDay(); // 日期 now.getMonth(); // 月份 now.getFu ...

  7. HDU5343 MZL's Circle Zhou(SAM+记忆化搜索)

    Problem Description MZL's Circle Zhou is good at solving some counting problems. One day, he comes u ...

  8. 最全的linux基础命令

    第1章 linux命令 1.1 线上查询及帮助命令 help命令*** help前面接你要查询的命令:例如ls [root@server02 ~]# ls --help 用法:ls [选项]... [ ...

  9. Asp.net Core 异常日志与API返回值处理

    需求: 1.对异常进行捕获记录日志 并且修改返回值给前端 解释: ILogger4是自定义的一个日志,更改它就好 解决方案1: 使用中间件进行异常捕获并且修改其返回值 public class Err ...

  10. 笔记||Python3之函数

    函数:          函数的概念:就是一段代码:一段操作流程. 优点:代码量少.简洁.   维护起来方便 -- 在函数的定义进行修改 函数的定义:1 - def 函数名(): 函数内容 2 - 函 ...