有赞一面:亿级用户DAU日活统计,有几种方案?

news2025/4/9 16:14:12

说在前面

在40岁老架构师 尼恩的读者社区(50+)中,最近有小伙伴拿到了一线互联网企业如极兔、有赞、希音、百度、网易、滴滴的面试资格,遇到一几个很重要的面试题:

(1) 亿级用户场景,如何高性能统计日活?

(2) 如何实现亿级数据统计?

(3) 亿级用户日活统计,有几种方案?

等等等等…

高并发Redis的使用,是面试的重点和高频点。

尼恩作为技术中台、数据中台的架构师,致力于为大家研究出一个 3高架构知识宇宙, 所以,这里,带大家完成一个 亿级用户场景,如何高性能统计日活的架构分析和实操。

当然,作为一篇文章,仅仅是抛砖引玉,后面有机会,带大家做一下这个高质量的实操,并且指导大家写入简历。

让面试官爱到 “不能自已、口水直流”

也一并把这个题目以及参考答案,收入咱们的 《尼恩Java面试宝典》V65版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从文末公号 【技术自由圈】取

文章目录

    • 说在前面
    • 业务场景分析
    • 亿级用户日活统计的存储架构
      • 方式1:通过 Redis 的 Set 集合来实现
      • 方式2:利用 Hash 类型实现
      • 方式3:利用 bitmap 实现
      • 方式4:利用 HyperLogLog 实现
    • 存储方案的问题分析
      • bigkey问题
      • 准确性问题
      • 方案选择
    • 回顾:什么是 Big Key?
      • Big Key的危害?
        • 1、阻塞请求
        • 2、内存增大
        • 3、阻塞网络
        • 4、影响主从同步、主从切换
    • HyperLogLog 原理和实操
      • 什么是 HyperLogLog?
      • HyperLogLog 与 bitmap 对比
        • 1、bitmap
        • 2、HyperLogLog
      • HyperLogLog 的使用场景
        • 大数据去重:
        • 用户活跃度统计:
        • 网站UV统计:
        • 社交网络推荐:
    • HyperLogLog 的相关命令
    • SpringBoot 实操:利用 HyperLogLog 实现网站UV统计
    • 超高并发异步架构
      • 队列缓存+批量写入的架构
    • 本文的版本计划和基础学习资料
    • 技术自由的实现路径 PDF:
        • 实现你的 架构自由:
        • 实现你的 响应式 自由:
        • 实现你的 spring cloud 自由:
        • 实现你的 linux 自由:
        • 实现你的 网络 自由:
        • 实现你的 分布式锁 自由:
        • 实现你的 王者组件 自由:
        • 实现你的 面试题 自由:

业务场景分析

什么是日活?

日活跃用户数量(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:

实现你的 架构自由:

《吃透8图1模板,人人可以做架构》

《10Wqps评论中台,如何架构?B站是这么做的!!!》

《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》

《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》

《100亿级订单怎么调度,来一个大厂的极品方案》

《2个大厂 100亿级 超大流量 红包 架构方案》

… 更多架构文章,正在添加中

实现你的 响应式 自由:

《响应式圣经:10W字,实现Spring响应式编程自由》

这是老版本 《Flux、Mono、Reactor 实战(史上最全)》

实现你的 spring cloud 自由:

《Spring cloud Alibaba 学习圣经》

《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》

《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》

实现你的 linux 自由:

《Linux命令大全:2W多字,一次实现Linux自由》

实现你的 网络 自由:

《TCP协议详解 (史上最全)》

《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》

实现你的 分布式锁 自由:

《Redis分布式锁(图解 - 秒懂 - 史上最全)》

《Zookeeper 分布式锁 - 图解 - 秒懂》

实现你的 王者组件 自由:

《队列之王: Disruptor 原理、架构、源码 一文穿透》

《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》

《缓存之王:Caffeine 的使用(史上最全)》

《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》

实现你的 面试题 自由:

4000页《尼恩Java面试宝典 》 40个专题

以上尼恩 架构笔记、面试题 的PDF文件更新,请到《技术自由圈》公号取↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/519044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

九、Ribbon负载均衡

目录 一、Ribbon负载均衡策略&#xff08;常用7种&#xff09;&#xff1a; 详细介绍&#xff1a; 1、轮询策略&#xff1a; RoundRibbonRule&#xff08;Ribbon默认策略&#xff09; 2、重试策略&#xff1a;RetryRule 3、加权响应时间策略&#xff1a;WeightedResponseT…

【模拟电子技术】常用半导体器件——本征半导体

&#x1f347;&#x1f347;只有强大的意志&#xff0c;才能成就最艰难的选择。 ——CSDN厉昱辰 目录 &#x1f34d;&#x1f34d;一、半导体 &#x1f34d;&#x1f34d;二、本征半导体的晶体结构 &#x1f34d;&#x1f34d; 三、本征半导体中的两种载流子 &#x1f34d…

数字图像处理基础知识(自救指南)

概念 本部分多来自课件&#xff0c;见水印 数字图像处理的目的 提高图像的视感质量&#xff0c;以达到赏心悦目的目的。提取图像中所包含的某些特征或特殊信息&#xff0c;主要用于计算机分析&#xff0c;经常用作模式识别、计算机视觉的预处理。对图像数据进行变换、编码和压…

炫云为什么要采用让人看不懂的GHZ计费?

很多人看到炫云GHZ计费都表示看不懂&#xff0c;觉得麻烦&#xff0c;没有按核数、按线程或者按分钟计费简单易懂&#xff0c;甚至还被某些同行经常拿来攻击。哪为什么炫云还坚持用GHZ计费呢&#xff1f;哪是因为使用GHZ计费更加公平、透明&#xff0c;且具有硬件无关性。今天就…

10个适合Web开发的最佳CSS生成器工具

在写CSS的时候&#xff0c;我们可以手写CSS&#xff0c;也可以直接使用一些CSS生成器来完成&#xff0c;今天我就跟大家分享10个好用的CSS生成器工具&#xff0c;帮助你提升工作效率。 1.纯CSS条纹生成器 网址&#xff1a;https://stripesgenerator.com 没有 Flash&#xff0…

QT多线程基础

文章目录 前言一、多线程概念介绍二、创建一个线程三、start和terminate函数四、如何不使用terminate函数终止线程总结 前言 本篇文章来讲解一下QT中的多线程使用方法。 其实线程这个概念对于我们来说并不陌生&#xff0c;main函数在多线程中一般就被称为主线程。 在QT中&am…

《计算机网络—自顶向下方法》 第四章Wireshark实验:TCP 协议分析

在因特网协议族&#xff08;Internet Protocol Suite&#xff09;中&#xff0c;TCP 层是位于 IP 层之上&#xff0c;应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接&#xff0c;但是 IP 层不提供这样的流机制&#xff0c;而是提供不可靠的包交换。…

Ordinal 是什么? BTC NFT 解释

Ordinal NFT 于 2023 年 1 月 20 日由开发者 Casey Rodarmor 在 BTC 主网上推出&#xff0c;它是在 BTC上 创建 NFT 的最新方式。虽然 BTC 上的 NFT 并不是一个新概念&#xff0c;BTC 之上的第 2 层网络&#xff08;例如 Counterparty 和 Stacks&#xff09;已经支持基于 BTC 的…

第七届中华梦乡·福清石竹山梦文化节举办

第七届中华梦乡福清石竹山梦文化节现场 5月9日至12日&#xff0c;第七届中华梦乡福清石竹山梦文化节暨海峡两岸&#xff08;福清&#xff09;道教论坛在福州福清举办。本届梦文化节以“福佑中华 梦圆石竹”为主题&#xff0c;旨在发挥海峡两岸道教界同根同源、联系密切的独特优…

【Java零基础入门篇】第 ⑤ 期 - 抽象类和接口(二)

博主&#xff1a;命运之光 专栏&#xff1a;Java零基础入门 学习目标 1.了解什么是抽象类&#xff0c;什么是接口&#xff1b; 2.掌握抽象类和接口的定义方法&#xff1b; 3.理解接口和抽象类的使用场景&#xff1b; 4.掌握多态的含义和用法&#xff1b; 5.掌握内部类的定义方法…

【HBase】介绍

文章目录 什么时候用Hbase&#xff1f;常见应用场景逻辑结构物理结构数据模型Name Space TableRowColumnTime StampCell 存储设计HLogHFileHfile生成方式 Apache HBase™ 是以 hdfs 为数据存储的&#xff0c;一种分布式、可扩展的 NoSQL 数据库。 Bigtable 是一个稀疏的、分布式…

5项目五:W1R3S-1(思路为主!)

特别注明&#xff1a;本文章只用于学习交流&#xff0c;不可用来从事违法犯罪活动&#xff0c;如使用者用来从事违法犯罪行为&#xff0c;一切与作者无关。 目录 前言 一、信息收集 二、网页信息的收集 三、提权 总结 前言 思路清晰&#xff1a; 1.信息收集&#xff0c;…

《程序员面试金典(第6版)》面试题 16.19. 水域大小(深度优先搜索,类似棋盘类问题,八皇后的简化版本,C++)

题目描述 你有一个用于表示一片土地的整数矩阵land&#xff0c;该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小&#xff0c;返回值需要从小到…

【数据结构与算法】图的遍历与拓扑排序

文章目录 一、用数组模拟邻接表二、图的深度优先遍历&#xff08;dfs&#xff09;2.1 概念2.2 例题&#xff1a;树的重心 三、图的广度优先遍历&#xff08;bfs&#xff09;3.1 概念3.2 例题&#xff1a;图中点的层次 四、拓扑排序4.1 概念4.2 例题&#xff1a;有向图的拓扑序列…

2直接连接的网络与VLAN划分-2.2【实验】【计算机网络】

2直接连接的网络与VLAN划分-2.2-2.3【实验】【计算机网络】 前言推荐2直接连接的网络与VLAN划分2.1共享式以太网和交换式以太网2.2交换机MAC地址表建立与帧转发实验目的实验内容及实验环境实验原理交换机通过逆向学习帧的源地址&#xff0c;构建MAC地址表;交换机中MAC地址的构建…

netty基础(五)

1.基本介绍 1>.异步的概念和同步相对.当一个异步过程调用发出后,调用者不能立刻得到结果.实际处理这个调用的组件在完成后,通过状态、通知和回调来通知调用者; 2>.Netty中的I/O操作是异步的,包括Bind、Write、Connect等操作会简单的返回一个 ChannelFuture; 3>.调用…

二、Eureka注册中心

目录 注册中心主要有三种角色&#xff1a; 1、服务提供者&#xff08;RPC Server&#xff09; 2、服务消费者&#xff08;RPC Client&#xff09; 3、服务注册中心&#xff08;Registry&#xff09; 注册中心实现原理 常用的注册中心&#xff1a; 常用注册中心对比&…

【C/C++每日一练】总目录(更新至2023.5.12)

C/C 2023.05 2023.5.11-2023.5.12 20230512 1. 成绩打印 ※ 2. 按要求补齐数组 &#x1f31f;&#x1f31f;&#x1f31f; 3. 水仙花数 ※ 20230511 1. 最长公共前缀 &#x1f31f; 2. 打家劫舍 &#x1f31f;&#x1f31f; 3. 最接近的三数之和 &#x1f31f;&am…

修改通过CDH安装的Kafka和Zookeeper等组件的最大文件句柄数

目录 1.查看当前kafka最大文件句柄数 2.修改supervisord.service 服务配置文件 3.加载配置文件 4.重启supervisord.service服务 5.重启kafka等组件 6.查看修改后最大文件句柄数 7.查看kafka当前使用的文件句柄数 参考&#xff1a; 1.查看当前kafka最大文件句柄数 jpsc…

【rust】| 05——语法基础 | 流程控制

系列文章目录 【rust】| 00——开发环境搭建 【rust】| 01——编译并运行第一个rust程序 【rust】| 02——语法基础 | 变量(不可变?)和常量 【rust】| 03——语法基础 | 数据类型 【rust】| 04——语法基础 | 函数 【rust】| 05——语法基础 | 流程控制 文章目录 流程控制1. 条…