有赞一面:亿级用户DAU日活统计,有几种方案?
Posted 疯狂创客圈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有赞一面:亿级用户DAU日活统计,有几种方案?相关的知识,希望对你有一定的参考价值。
文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
有赞一面:亿级用户日活统计,有几种方案?
说在前面
在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如极兔、有赞、希音、百度、网易、滴滴的面试资格,遇到一几个很重要的面试题:
(1) 亿级用户场景,如何高性能统计日活?
(2) 如何实现亿级数据统计?
(3) 亿级用户日活统计,有几种方案?
等等等等......
高并发Redis的使用,是面试的重点和高频点。
尼恩作为技术中台、数据中台的架构师,致力于为大家研究出一个 3高架构知识宇宙, 所以,这里,带大家完成一个 亿级用户场景,如何高性能统计日活的架构分析和实操。
当然,作为一篇文章,仅仅是抛砖引玉,后面有机会,带大家做一下这个高质量的实操,并且指导大家写入简历。
让面试官爱到 “不能自已、口水直流”。
也一并把这个题目以及参考答案,收入咱们的 《尼恩Java面试宝典》V65版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从公众号 【技术自由圈】获取,暗号:领电子书
本文目录
业务场景分析
什么是日活?
日活跃用户数量(Daily Active User,DAU)是用于反映网站、互联网应用或网络游戏的运营情况的统计指标。
日活跃用户数量通常统计一日(统计日)之内,登录或使用了某个产品的用户数(去除重复登录的用户)。
受统计方式限制,互联网行业使用的日均活跃用户数指在统计周期(周/月)内,该App的每日活跃用户数的平均值。
亿级用户日活统计的存储架构
一个用户一天内多次访问一个网站只能算作一次,
怎么做存储架构呢?
大概有四种方案,可供技术选型:
- 方式1:通过 Redis 的 Set 集合来实现。
- 方式2:利用 Hash 类型实现
- 方式3:利用 bitmap 实现
- 方式4:利用 HyperLogLog 实现
方式1:通过 Redis 的 Set 集合来实现
将用户id 放到 Set 中,Set 的去重功能能保证不会重复记录同一个用户 ID。
一个ZSET类型的Key,它的成员数量为10000个
一般来说,这个ZSET就是bigkey
方式2:利用 Hash 类型实现
将用户 ID 作为 Hash 集合的 key,访问页面则执行 HSET
命令将 value 设置成 1。
即使用户重复访问,重复执行命令,也只会把这个 userId 的值设置成 “1"。
利用 HLEN
命令,统计 Hash 集合中的元素个数就是 UV。
一个Hash类型的Key,它的成员数量虽然只有1000个但这些成员的Value值总大小为100MB,
一般来说,这个 Hash 就是bigkey
方式3:利用 bitmap 实现
使用bitmap,记录用户id的访问,则把指定key的用户id对应的bit位标记为1,
统计 日活,就是就是指定key中1的个数。
显然这里是操作位,相比2中,一个字节为8位,估算一下占用空间:96M/8=12M,
1千万用户只要 1M多的空间,但是 1亿的用户需要12M的大小即可记录。
一个String类型的Key,它的值为5MB, 就是bigkey
bitmap 底层结构,和String类型是类似的。
方式4:利用 HyperLogLog 实现
在输入用户数量非常大时,计算基数所需要的空间总是固定的,并且是很小的,
每个hyperloglog键需要12kb内存可以计算2^64个不同元素的基数,
这和元素基数元素越多,耗费内存越多的集合形成鲜明对比,
但是hyperloglog只会根据元素来计算基数,不会存储元素本身,所以不能像其它类型一样返回各个元素
hyperloglog是概率算法,是牺牲准确率换区空间的,
对于对精度要求不高的情况下可以使用,因为概率算法本身不直接存储数据本身,能保证误差在一定范围内,又不占用空间,误差在0.81%左右
存储方案的问题分析
bigkey问题
方案1、方案2、方案3
准确性问题
方案4
方案选择
如果允许 存在 准确性问题,就使用 hyperloglog 存储架构
如果允许 存在 bigkey 问题,或者解决 bigkey 问题,就使用 方案3 存储架构
这里优先使用 hyperloglog 存储架构
回顾:什么是 Big Key?
通俗易懂的讲,Big Key就是某个key对应的value很大,占用的redis空间很大,本质上是大value问题。
key往往是程序可以自行设置的,value往往不受程序控制,因此可能导致value很大。
redis中这些Big Key对应的value值很大,在序列化/反序列化过程中花费的时间很大,因此当我们操作Big Key时,通常比较耗时,这就可能导致redis发生阻塞,从而降低redis性能。
BigKey指以Key的大小和Key中成员的数量来综合判定,用几个实际的例子对大Key的特征进行描述:
- Key本身的数据量过大:一个String类型的Key,它的值为5MB
- Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10000个
- Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1000个但这些成员的Value值总大小为100MB
Big Key的危害?
1、阻塞请求
Big Key对应的value较大,我们对其进行读写的时候,需要耗费较长的时间,这样就可能阻塞后续的请求处理。Redis的核心线程是单线程,单线程中请求任务的处理是串行的,前面的任务完不成,后面的任务就处理不了。
2、内存增大
读取Big Key耗费的内存比正常Key会有所增大,如果不断变大,可能会引发 OOM(内存溢出),或达到redis的最大内存maxmemory设置值引发写阻塞或重要Key被逐出。
3、阻塞网络
读取单value较大时会占用服务器网卡较多带宽,自身变慢的同时可能会影响该服务器上的其他Redis实例或者应用。
4、影响主从同步、主从切换
删除一个大Key造成主库较长时间的阻塞并引发同步中断或主从切换。
HyperLogLog 原理和实操
HyperLogLog 是大数据基数统计中的常见方法,无论是 Redis,Spark 还是 Flink 都提供了这个功能,其目的就是在一定的误差范围内,用最小的空间复杂度来估算一个数据流的基数。
这个最小的空间,小到什么程度呢?在 Redis 中实现的 HyperLogLog,只需要12K内存就能统计2^64个数据。不过有得必有失,HyperLogLog计数存在一定的误差,误差率整体较低。标准误差为 0.81% ,不到1%。当然,误差可以被设置辅助计算因子进行降低。
什么是 HyperLogLog?
HyperLogLog 是一种概率性数据结构,它是 LogLog 算法的升级版,作用是能够提供不精确的去重计数。HyperLogLog 下面简称为HLL,
Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。
HLL是一种用于基数统计的算法,可以用来估计一个集合中不同元素的数量,而不需要存储这些元素本身。它可以高效地处理大规模的数据集,且占用的空间非常小,通常只需要几千个字节的存储空间就可以处理数十亿个元素。HLL算法的实现非常简单,可以使用位图和随机哈希函数来实现。
HLL算法的基本思想是利用随机哈希函数将元素映射到一个二进制字符串中,并根据哈希函数的结果将字符串分为若干个桶。在每个桶中,记录哈希值的最大前导零的数量。
对于一个大的数据集,可以对每个元素进行哈希并记录其对应桶的最大前导零的数量,然后计算所有桶的平均值,得到一个近似的基数估计值。
HLL算法的误差率可以控制在0.81/sqrt(m)以内,其中m是桶的数量。
因此,随着桶的数量增加,误差率将变得越来越小。
HLL算法的优点在于它具有极低的内存消耗和高效的计算速度,并且可以处理极大的数据集。
HyperLogLog 与 bitmap 对比
1、bitmap
优势是:非常均衡的特性,精准统计,可以得到每个统计对象的状态,秒出。
缺点是:当你的统计对象数量十分十分巨大时,可能会占用到一点存储空间,但也可在接受范围内。也可以通过分片,或者压缩的额外手段去解决。
注意:bitmap是精确的
2、HyperLogLog
优势是:
可以统计夸张到无法想象的数量,并且占用小的夸张的内存。
缺点是:
建立在牺牲准确率的基础上,而且无法得到每个统计对象的状态。
注意:HyperLogLog 不是精确的,误差在 1%左右
HyperLogLog 的使用场景
HyperLogLog算法主要用于基数统计场景,即需要快速统计一个数据集中不同元素的数量的场合。在实际应用中,HyperLogLog算法通常应用于以下场景:
大数据去重:
在大数据场景下,需要去重的数据量非常大,如果使用传统的去重算法,需要对每个元素进行存储和比对,时间和空间消耗非常高。HyperLogLog算法可以在占用极小的空间的情况下,高效地对大规模数据进行去重,提高去重效率。
用户活跃度统计:
在Web应用和移动应用中,需要对用户的活跃度进行统计。如果每个用户的活跃度都进行存储,需要消耗大量的存储空间。HyperLogLog算法可以在占用极小的空间的情况下,高效地统计活跃用户数量,提高统计效率。
网站UV统计:
在Web应用中,需要统计网站每日独立访客数量(即UV),但是由于数据量非常大,不能简单地直接计数,因为会导致内存不足。HyperLogLog算法可以在占用极小的空间的情况下,高效地对大规模的访问日志进行去重和统计,提高统计效率。
社交网络推荐:
在社交网络中,需要对用户的兴趣爱好进行统计,以便向用户推荐相关内容。HyperLogLog算法可以在占用极小的空间的情况下,高效地对用户行为进行去重和统计,提高推荐效率。
总之,HyperLogLog算法可以在需要高效处理大规模数据集的场景下发挥作用,适用于各种基数统计场合,可以提高数据处理效率,减少存储空间的消耗。
HyperLogLog 的相关命令
Redis提供了多个HyperLogLog相关的指令,包括以下几个:
- PFADD:将一个或多个元素添加到指定的HyperLogLog数据结构中。
语法:PFADD key element [element ...]
PFADD hll_key a b c
- PFCOUNT:统计指定的HyperLogLog数据结构中不同元素的数量。
语法:PFCOUNT key [key ...]
PFCOUNT hll_key
- PFMERGE:将多个HyperLogLog数据结构合并为一个。
语法:PFMERGE destkey sourcekey [sourcekey ...]
PFMERGE hll_key1 hll_key2 hll_key3
这些指令可以方便地对HyperLogLog数据结构进行添加、统计和合并操作,以满足各种基数统计场景的需求。
需要注意的是,HyperLogLog数据结构虽然占用空间非常小,但是在添加元素时需要进行哈希计算,因此添加元素的效率可能会受到影响。
因此,在java代码中,可以使用 队列缓存+ 批量写入的架构,进行批量添加, 尽量减少io时间。
在使用HyperLogLog数据结构时,需要根据实际情况评估其效率和误差率,以确定是否适合使用HyperLogLog算法。
SpringBoot 实操:利用 HyperLogLog 实现网站UV统计
利用HyperLogLog实现网站UV统计 ,代码如下:
测试如下:
误差计算:
1百万,减去 99 6723 ,大概在 4000左右,不到1%
超高并发异步架构
通过阻塞队列,使用队列缓存+批量写入的架构
队列缓存+批量写入的架构
这里用了阻塞队列,这个非常常用
具体的架构图如下:
所以阻塞队列的结构,以及底层原理,大家好好掌握
BlockingDeque<String> dauList = new LinkedBlockingDeque<>();
访问记录,直接进入到 阻塞队列,参考代码如下
异步写入的参考代码如下:
本文的版本计划和基础学习资料
尼恩作为技术中台、数据中台的架构师, 高并发是尼恩架构的重点, 所以,后面会大家从 架构到实操, 多个维度、多种场景的高并发实操。
而且尼恩指导简历的过程中,也指导过小伙伴写过 10Wqps 超高并发 网关、超高并发秒杀、超高并发物联网等等简历,里边涉及到大量的设计模式,
经过尼恩的指导之后,很多 小伙伴的简历,里边立马金光闪闪,并且顺利走上了架构师之路。
后面有机会,带大家做一下这个高质量的实操,并且指导大家写入简历。
所以,这个材料后面也会进行持续迭代,大家可以找尼恩来获取最新版本和技术交流。
尼恩编著的400页PDF电子书 《SpringCloud Alibaba 技术圣经》
尼恩编著的500页 PDF电子书 《Java高并发核心编程 卷2 加强版》,清华大学出版社出版
pdf领取方式,请参见公众号: 技术自由圈
技术自由的实现路径:
实现你的 架构自由:
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
… 更多架构文章,正在添加中
实现你的 响应式 自由:
这是老版本 《Flux、Mono、Reactor 实战(史上最全)》
实现你的 spring cloud 自由:
《Spring cloud Alibaba 学习圣经》 PDF
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
实现你的 linux 自由:
实现你的 网络 自由:
《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》
实现你的 分布式锁 自由:
实现你的 王者组件 自由:
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》
实现你的 面试题 自由:
免费获取11个技术圣经PDF:
有赞百亿级日志系统架构设计
文 | 饶榕 on 中间件
一、概述
日志是记录系统中各种问题信息的关键,也是一种常见的海量数据。日志平台为集团所有业务系统提供日志采集、消费、分析、存储、索引和查询的一站式日志服务。主要为了解决日志分散不方便查看、日志搜索操作复杂且效率低、业务异常无法及时发现等等问题。
随着有赞业务的发展与增长,每天都会产生百亿级别的日志量(据统计,平均每秒产生 50 万条日志,峰值每秒可达 80 万条)。日志平台也随着业务的不断发展经历了多次改变和升级。本文跟大家分享有赞在当前日志系统的建设、演进以及优化的经历,这里先抛砖引玉,欢迎大家一起交流讨论。
二、原有日志系统
有赞从 16 年就开始构建适用于业务系统的统一日志平台,负责收集所有系统日志和业务日志,转化为流式数据,通过 flume 或者 logstash 上传到日志中心(kafka 集群),然后共 Track、Storm、Spark 及其它系统实时分析处理日志,并将日志持久化存储到 HDFS 供离线数据分析处理,或写入 ElasticSearch 提供数据查询。整体架构如图 2-1 所示。
图2-1 原有日志系统架构
随着接入的应用的越来越多,接入的日志量越来越大,逐渐出现一些问题和新的需求,主要在以下几个方面:
业务日志没有统一的规范,业务日志格式各式各样,新应用接入无疑大大的增加了日志的分析、检索成本。
多种数据日志数据采集方式,运维成本较高。
日志平台收集了大量用户日志信息,当时无法直接的看到某个时间段,哪些错误信息较多,增加定位问题的难度。
存储方面:
采用了 Es 默认的管理策略,所有的 index 对应 3*2 shard(3 个 primary,3 个 replica),有部分 index 数量较大,对应单个 shard 对应的数据量就会很大,导致有 hot node,出现很多 bulk request rejected,同时磁盘 IO 集中在少数机器上;
对于 bulk request rejected 的日志没有处理,导致业务日志丢失;
日志默认保留 7 天,对于 ssd 作为存储介质,随着业务增长,存储成本过于高昂;
另外 Elasticsearch 集群也没有做物理隔离,Es 集群 oom 的情况下,使得集群内全部索引都无法正常工作,不能为核心业务运行保驾护航。
三、现有系统演进
日志从产生到检索,主要经历以下几个阶段:采集->传输->缓冲->处理->存储->检索,详细架构如图 3-1 所示
图3-1 现有系统架构
3.1日志接入
日志接入目前分为两种方式,SDK 接入和调用 Http Web 服务接入
SDK 接入:日志系统提供了不同语言的 SDK,SDK 会自动将日志的内容按照统一的协议格式封装成最终的消息体,并最后最终通过 TCP 的方式发送到日志转发层(rsyslog-hub);
Http Web 服务接入:有些无法使用 SDk 接入日志的业务,可以通过 Http 请求直接发送到日志系统部署的 Web 服务,统一由 web protal 转发到日志缓冲层的 kafka 集群。
3.2日志采集
现在有 rsyslog-hub 和 web portal 做为日志传输系统,rsyslog 是一个快速处理收集系统日志的程序,提供了高性能、安全功能和模块化设计。之前系统演进过程中使用过直接在宿主机上部署 flume 的方式,由于 flume 本身是 java 开发的,会比较占用机器资源而统一升级为使用 rsyslog 服务。为了防止本地部署与 kafka 客户端连接数过多,本机上的 rsyslog 接收到数据后,不做过多的处理就直接将数据转发到 rsyslog-hub 集群,通过 LVS 做负载均衡,后端的 rsyslog-hub 会通过解析日志的内容,提取出需要发往后端的 kafka topic。
3.3日志缓冲
Kafka 是一个高性能、高可用、易扩展的分布式日志系统,可以将整个数据处理流程解耦,将 kafka 集群作为日志平台的缓冲层,可以为后面的分布式日志消费服务提供异步解耦、削峰填谷的能力,也同时具备了海量数据堆积、高吞吐读写的特性。
3.4日志切分
日志分析是重中之重,为了能够更加快速、简单、精确地处理数据。日志平台使用 spark streaming 流计算框架消费写入 kafka 的业务日志,Yarn 作为计算资源分配管理的容器,会跟不同业务的日志量级,分配不同的资源处理不同日志模型。
整个 spark 任务正式运行起来后,单个批次的任务会将拉取的到所有的日志分别异步的写入到 ES 集群。业务接入之前可以在管理台对不同的日志模型设置任意的过滤匹配的告警规则,spark 任务每个 excutor 会在本地内存里保存一份这样的规则,在规则设定的时间内,计数达到告警规则所配置的阈值后,通过指定的渠道给指定用户发送告警,以便及时发现问题。当流量突然增加,es 会有 bulk request rejected 的日志会重新写入 kakfa,等待补偿。
3.5日志存储
原先所有的日志都会写到 SSD 盘的 ES 集群,logIndex 直接对应 ES 里面的索引结构,随着业务增长,为了解决 Es 磁盘使用率单机最高达到 70%~80% 的问题,现有系统采用 Hbase 存储原始日志数据和 ElasticSearch 索引内容相结合的方式,完成存储和索引;
Index 按天的维度创建,提前创建index会根据历史数据量,决定创建明日 index 对应的 shard 数量,也防止集中创建导致数据无法写入。现在日志系统只存近 7 天的业务日志,如果配置更久的保存时间的,会存到归档日志中;
对于存储来说,Hbase、Es 都是分布式系统,可以做到线性扩展。
四、多租户
随着日志系统不断发展,全网日志的 QPS 越来越大,并且部分用户对日志的实时性、准确性、分词、查询等需求越来越多样。为了满足这部分用户的需求,日志系统支持多租户的的功能,根据用户的需求,分配到不同的租户中,以避免相互影响。
针对单个租户的架构如下:
SDK:可以根据需求定制,或者采用天网的 TrackAppender 或 SkynetClient;
Kafka 集群:可以共用,也可以使用指定 Kafka 集群;
Spark 集群:目前的 Spark 集群是在 yarn 集群上,资源是隔离的,一般情况下不需要特地做隔离;
存储:包含 ES 和 Hbase,可以根据需要共用或单独部署 ES 和 Hbase。
五、现有问题和未来规划
目前,有赞日志系统作为集成在天网里的功能模块,提供简单易用的搜索方式,包括时间范围查询、字段过滤、NOT/AND/OR、模糊匹配等方式,并能对查询字段高亮显示,定位日志上下文,基本能满足大部分现有日志检索的场景,但是日志系统还存在很多不足的地方,主要有:
缺乏部分链路监控:日志从产生到可以检索,经过多级模块,现在采集,日志缓冲层还未串联,无法对丢失情况进行精准监控,并及时推送告警。
现在一个日志模型对应一个 kafka topic,topic 默认分配三个 partition,由于日志模型写入日志量上存在差异,导致有的 topic 负载很高,有的 topic 造成一定的资源浪费,且不便于资源动态伸缩。topic 数量过多,导致partition 数量过多,对 kafka 也造成了一定资源浪费,也会增加延迟和 Broker 宕机恢复时间。
目前 Elasticsearch 中文分词我们采用 ikmaxword,分词目标是中文,会将文本做最细粒度的拆分,但是日志大部分都是英文,分词效果并不是很好。
上述的不足之处也是我们以后努力改进的地方,除此之外,对于日志更深层次的价值挖掘也是我们探索的方向,从而为业务的正常运行保驾护航。
文末福利
4月27日(周六)下午13:30
有赞技术中间件团队联合Elastic中文社区
围绕Elastic的开源产品及周边技术
在杭州举办一场线下技术交流活动
本次活动免费开放,限额200名
扫描下图二维码,回复“报名”即可参加
欢迎参加,咱们一起聊聊~
-The End-
Vol.177
有赞技术团队
为 442 万商家,150 个行业,330 亿电商交易额
提供技术支持
微商城|零售|美业 | 教育
技术博客:tech.youzan.com
The bigger the dream,
the more important the team.
以上是关于有赞一面:亿级用户DAU日活统计,有几种方案?的主要内容,如果未能解决你的问题,请参考以下文章
亿级日活业务稳如磐石,华为云CodeArts PerfTest发布
HyperLogLog 算法原理及其在 Redis 中的实现
打造极致体验:字节跳动亿级 DAU 背后的音视频技术最佳实践