架构设计内容分享(六十二):亿级用户日活统计,有几种方案?

目录

业务场景分析

亿级用户日活统计的存储架构

存储方案的问题分析

回顾:什么是 Big Key?

HyperLogLog 原理和实操

HyperLogLog 的相关命令

SpringBoot实操:利用HyperLogLog实现网站UV统计

超高并发异步架构


业务场景分析

什么是日活?

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

访问记录,直接进入到 阻塞队列,参考代码如下

 异步写入的参考代码如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值