基础编程学习快乐每一天
首页
留言
Siddim.com
当前位置:
首页
>
编程知识库
>
后端开发知识
>
面试官:说一下使用 Redis 实现大规模的帖子浏览计数的思路
面试官:说一下使用 Redis 实现大规模的帖子浏览计数的思路
阅读
1
2020-05-17
本文翻译自全球访问量排名第
8
位的论坛
Reddit
博客上的文章,讲的是关于
Reddit
如何在海量浏览量下实时统计浏览量的。
本文我们就来聊一聊,
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
和内存性能的担忧。
Reddit
的数据管道,主要都是使用
Apache
Kafka
的。每当一个用户浏览一篇文章时,就会触发一个事件并且被发送到事件收集服务器,然后批量的将这些事件发送打
kafka
中进行持久化。
Reddit
的浏览统计系统,分为两个顺序执行的组成部分,其中的第一部分是,被称为
Nazar
的
kafka
队列『消费者』(
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
秒一组批次进行写入。下图就是整个过程的流程图。
来源:
https
://
www
.
jianshu
.
com
/
p
/
523635f5f133
最近五期
【
85
期】谈谈
Java
面向对象设计的六大原则,中高级面试常问!
【
86
期】五个刁钻的
String
面试问题及解答
【
87
期】面试官问:
Java
序列化和反序列化为什么要实现
Serializable
接口
【
88
期】面试官问:你能说说
Spring
中,接口的
bean
是如何注入的吗?
【
89
期】面试官
5
连问一个
TCP
连接可以发多少个
HTTP
请求?
? ~
以上数据来源于网络,如有侵权,请联系删除。
上一篇:
面试官 5 连问一个 TCP 连接可以发多少个 HTTP 请求?
下一篇:
面试官:Spring 用了哪些设计模式?说三种即可
评论
(0)
提交
类别
基础编程学习
HTML
PHP
Python
编程知识库
后端开发知识
热门文章
Java并发中的同步容器与并发容器,你了解多少?
Innodb中的事务隔离级别和锁的关系,难倒一半面试者!
SpringBoot + minio实现分片上传、秒传、续传
面试官:你知道消息队列如何保证数据不丢失吗?
JAVA知识 Java8新特性
面试官:谈谈为什么要限流,有哪些限流方案?
说说动态代理与静态代理区别
面试官:思考Tomcat 类加载器为什么要违背双亲委派模型?
boot-admin 基于SpringBoot的后台权限管理系统,可作为脚手架,用于快速搭建项目
SpringBoot+Vue+App+硬件实现智能家居系统项目