如何使用 Redis 实现大规模的帖子浏览计数

点击上方“芋道源码”,选择“置顶公众号”

技术文章第一时间送达!

源码精品专栏

 

来源:http://t.cn/EL1FB0M

  • 统计方法


英文原文

本文翻译自全球访问量排名第8位的论坛Reddit博客上的文章,讲的是关于Reddit如何在海量浏览量下实时统计浏览量的。

640
img

本文我们就来聊一聊,Reddit 是如何在大规模下统计帖子浏览量的。

统计方法

我们对统计浏览量有四个基本的要求

  • 计数必须达到实时或者接近实时。

  • 每个用户在一个时间窗口内仅被记录一次。

  • 帖子显示的统计数量的误差不能超过百分之几。

  • 整个系统必须能在生成环境下,数秒内完成阅读计数的处理。

满足上面四个条件,其实比想象中要复杂。为了在实时统计的情况下保持精准度,我们需要知道某一个用户之前是否浏览过一篇文章,所以我们需要为每一篇文章存储浏览过它的用户的集合,并且在每次新增浏览时检查该集合进行去重复操作。

一个比较简单的解决方案是,为每篇文章维护一个哈希表,用文章ID作为key,去重的userid的集合(set数据结构)作为value。

这种方案在文章数量和阅读数比较小的情况下,还能很好的运行,但当数据量到达大规模时,它就不适用了。尤其是该文章变成了热门文章,阅读数迅速增长,有些受欢迎的文章的阅读者数量超过百万级别,想象一下维护一个超过百万的unqine userId的集合在内存中的,还有经受住不断的查询,集合中的用户是否存在。

自从我们决定不提供100%精准的数据后,我们开始考虑使用几种不同的基数估计算法。我们综合考虑下选出量两个可以满足需求的算法:

  • 线性概率计算方法,它非常精确,但是需要的内存数量是根据用户数线性增长的。

  • 基于HyperLogLog (HLL)的计算方法,HLL的内存增长是非线性的,但是统计的精准度和线性概率就不是同一级别的了。

为了更好的理解基于HLL的计算方法,究竟能够节省多少内存,我们这里使用一个例子。考虑到r/pics文章,在本文开头提及,该文章收到了超过一百万用户的浏览过,如果我们存储一百万个唯一的用户ID,每一个id占用8个字节,那么仅仅一篇文章就需要8mb的空间存储!对照着HLL所需要的存储空间就非常少了,在这个例子中使用HLL计算方法仅需要 12kb的空间也就是第一种方法的0.15%。

(This article on High Scalability 这篇文章讲解了上面的两种算法.)

有很多的HLL实现是基于上面两种算法的结合而成的,也就是一开始统计数量少的情况下使用线性概率方法,当数量达到一定阈值时,切换为HLL方法。这种混合方法非常有用,不但能够为小量数据集提供精准性,也能为大量数据节省存储空间。该种实现方式的细节请参阅论文(Google’s HyperLogLog++ paper)

HLL算法的实现是相当标准的,这里有三种不同的实现方式,要注意的是,基于内存存储方案的HLL,这里我们只考虑Java和Scale两种实现

  • Twitter的Algebird库,Scala实现,Algebird的文档撰写非常好,但是关于它是如何实现HLL的,不是很容易理解。

  • stream-lib库中的HyperLogLog++实现,Java编写。 stream-lib代码的文档化做的很好,但我们对如何适当调优它,还是有些困惑的。

  • Redis的HLL实现(我们最终的选择),我们觉得Redis的实现不管从文档完善程度还是配置和提供的API接口,来说做的都非常好。另外的加分点是,使用Redis可以减少我们对CPU和内存性能的担忧。

640
img

Reddit的数据管道,主要都是使用Apache Kafka的。每当一个用户浏览一篇文章时,就会触发一个事件并且被发送到事件收集服务器,然后批量的将这些事件发送打kafka中进行持久化。

Reddit的浏览统计系统,分为两个顺序执行的组成部分,其中的第一部分是,被称为Nazarkafka队列『消费者』(consumer) ,它会从kafka中读取事件,然后将这些事件通过特定的条件进行过滤,判断改事件是否应该被算作一次文章阅读计数,它被称为『NAZAR』是因为在系统中它有作为『眼镜』的用处,识别出哪些事件是不应该被加入到统计中的。Nazar使用Redis 维护状态还有一个事件不被计数的潜在原因,这个原因可能是用户短时间内重复浏览统一文章。Nazar会在事件被发送回kafka时,为事件添加一个标识位,根据该事件是否被加入到计数当中的布尔值。

统计系统的第二部是一个称为Abacus 的kafka『消费者』它会真正的统计浏览量,并且让浏览量数据可以在整站和客户端上显示, 它接收从Nazar发送出来的事件消息,然后根据该消息中包含着标识值(Nazar中处理的)来判断这个事件是否算做一次计数,如果事件被计数,Abacus会首先检查这个事件中文章的HLL计数是否存在于Redis中,如果存在,Abacus会发送一个PFADD请求给Redis,如果不存在,Abacus会发生一个请求到Cassandra集群,Cassandra集群会持久化HLL 计数和真实的原始计数数据,然后再发送一个SET请求到Redis,这个过程通常出现在用户阅读一个已经被Redis剔除的就文章的情况下发送。

为了让维护一个在Redis可能被剔除的旧文章,Abacus会定期的,从Redis中将HLL过滤数据,包括每篇文章的计数,全部写入到Cassandra集群中,当然为了避免集群过载,这个步骤会分为每篇文章10秒一组批次进行写入。下图就是整个过程的流程图。

640
img




欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

640

已在知识星球更新源码解析如下:

  • 《精尽 Dubbo 源码解析系列》69 篇。

  • 《精尽 Netty 源码解析系列》61 篇。

  • 《精尽 Spring 源码解析系列》35 篇。

  • 《精尽 Spring MVC 源码解析系列》24 篇。

  • 《精尽 MyBatis 源码解析系列》34 篇。

  • 《数据库实体设计》17 篇。

  • 《精尽面试题》6 篇。持续更新...

  • 《精尽学习指南》6 篇。持续更新...


目前在知识星球更新了《精尽面试题》目录如下:

01. Dubbo 面试题

02. Netty 面试题

03. Spring 面试题

04. Spring MVC 面试题

05. Spring Boot 面试题

06. MyBatis 面试题


目前在知识星球更新了《精尽学习指南》目录如下:

01. Dubbo 学习指南

02. Netty 学习指南

03. Spring 学习指南

04. Spring MVC 学习指南

05. Spring Boot 学习指南

06. MyBatis 学习指南


目前在知识星球更新了《Dubbo 源码解析》目录如下:

01. 调试环境搭建
02. 项目结构一览
03. 配置 Configuration
04. 核心流程一览

05. 拓展机制 SPI

06. 线程池

07. 服务暴露 Export

08. 服务引用 Refer

09. 注册中心 Registry

10. 动态编译 Compile

11. 动态代理 Proxy

12. 服务调用 Invoke

13. 调用特性 

14. 过滤器 Filter

15. NIO 服务器

16. P2P 服务器

17. HTTP 服务器

18. 序列化 Serialization

19. 集群容错 Cluster

20. 优雅停机

21. 日志适配

22. 状态检查

23. 监控中心 Monitor

24. 管理中心 Admin

25. 运维命令 QOS

26. 链路追踪 Tracing

... 一共 69+ 篇

目前在知识星球更新了《Netty 源码解析》目录如下:

01. 调试环境搭建
02. NIO 基础
03. Netty 简介
04. 启动 Bootstrap

05. 事件轮询 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 字节缓冲区 ByteBuf

09. 通道处理器 ChannelHandler

10. 编解码 Codec

11. 工具类 Util

... 一共 61+ 篇


目前在知识星球更新了《数据库实体设计》目录如下:


01. 商品模块
02. 交易模块
03. 营销模块
04. 公用模块

... 一共 17+ 篇


目前在知识星球更新了《Spring 源码解析》目录如下:


01. 调试环境搭建
02. IoC Resource 定位
03. IoC BeanDefinition 载入

04. IoC BeanDefinition 注册

05. IoC Bean 获取

06. IoC Bean 生命周期

... 一共 35+ 篇


目前在知识星球更新了《Spring MVC 源码解析》目录如下:


01. Spring MVC 面试题
02. Spring MVC 学习指南
03. 调试环境搭建
04. 容器的初始化
05. 组件一览
06. 请求处理一览
07. HandlerMapping 组件
08. HandlerAdapter 组件
09. HandlerExceptionResolver 组件
10. RequestToViewNameTranslator 组件
11. LocaleResolver 组件
12. ThemeResolver 组件
13. ViewResolver 组件

14. MultipartResolver 组件

15. FlashMapManager 组件

... 一共 24+ 篇


目前在知识星球更新了《MyBatis 源码解析》目录如下:


01. 调试环境搭建
02. 项目结构一览
03. MyBatis 面试题合集

04. MyBatis 学习资料合集

05. MyBatis 初始化

06. SQL 初始化

07. SQL 执行

08. 插件体系

09. Spring 集成

... 一共 34+ 篇


源码不易↓↓↓

点赞支持老艿艿↓↓

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值