之前,统计每篇博文的阅读数的方式是经过筛选去重之后直接更新数据库,并发压力直接传导到数据库,假设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. 以Python为例的Async / Await的编程基础

    来源:Redislabs 作者:Loris Cro 翻译:Kevin (公众号:中间件小哥) 近年来,许多编程语言都在努力改进它们的并发原语.Go 语言有 goroutines,Ruby 有 fibe ...

  2. Python 代码块

    代码块 骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9925021.html https://www.cnblogs.com/jin-xin/articles/ ...

  3. socket实现ftp上传下载

    socket实现ftp文件的上传和下载 server端代码: import socket import json import struct import os soc = socket.socket ...

  4. LeetCode 5282. 转化为全零矩阵的最少反转次数

    地址 https://leetcode-cn.com/submissions/detail/39277402/ 题目描述给你一个 m x n 的二进制矩阵 mat. 每一步,你可以选择一个单元格并将它 ...

  5. webpack4.0安装及使用(一)

    前言  1.什么是webpack 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler).当 webpack 处理应用程序时,它会递归地构建 ...

  6. luogu P5596 【XR-4】题

    题目背景 xht37 喜欢分块,以至于对一道不需要分块的题也要分块做. 题目描述 有一个长度为 nn 的序列,xht37 现在想分块维护它. PinkRabbit 要求他只准将序列分成 PRPR 种长 ...

  7. 移动开发在路上-- IOS移动开发系列 多线程三

    这一次说一点概念性的东西,也是为后边做一些基础 HTTP协议的基本概念 http协议的基本概念 全称“超文本传输协议”,浏览器和服务器之间的通信规则 HTTp协议永远都是客户端发起的请求,服务器回送响 ...

  8. mongodb验证

    mongodb默认是不需要用户名和密码就可以增删查改的.要设置成需要用户名和密码访问,步骤如下: 通过下面的命令启动mongodb服务器 mongod 启动之后再登录,执行下面命令: use test ...

  9. art-template循环无法显示出数据

    art-template循环遍历无法显示数据原因 1.语法问题:循环语句导致的问题 2.插件问题: 用标准语法时循环数据如果引入第一个插件,会导致数据显示不出来只有引入第二个插件才可循环出数据 用原生 ...

  10. 【Web技术】314- 前端组件设计原则

    点击上方"前端自习课"关注,学习起来~ 译者:@没有好名字了译文:https://github.com/lightningminers/article/issues/36,http ...