丁香医生 2025-12-15 11:15 采纳率: 99.1%
浏览 0
已采纳

如何高效实现基于Access Count的去重统计?

在高并发场景下,如何高效实现基于 Access Count 的去重统计是一个常见挑战。典型问题是:用户频繁访问同一资源,需实时统计每个资源的独立访问用户数(UV),同时避免重复计数。传统方案如全量日志落盘后离线处理延迟高,而直接使用 Redis Set 存储用户 ID 虽可实现实时去重,但内存消耗大、成本高。尤其当用户量巨大时,单一 Redis 实例易成为瓶颈。如何在保障低延迟统计的同时,平衡内存占用与系统性能?是否可通过布隆过滤器、HyperLogLog 或分片优化等手段提升去重效率?这是实际架构中亟待解决的关键问题。
  • 写回答

1条回答 默认 最新

  • 玛勒隔壁的老王 2025-12-15 11:20
    关注

    高并发场景下基于 Access Count 的去重统计架构演进与优化策略

    1. 问题背景与核心挑战

    在现代互联网系统中,用户行为数据的实时统计是运营、推荐和风控等模块的基础能力。其中,“独立访问用户数”(UV)作为衡量资源热度的核心指标,其准确性与实时性至关重要。典型场景如短视频平台的视频播放 UV、电商平台的商品详情页 UV 等。

    然而,在高并发环境下,传统方案面临显著瓶颈:

    • 全量日志落盘 + 离线处理:延迟高,无法满足秒级甚至毫秒级响应需求;
    • Redis Set 存储用户ID:虽可实现精确去重,但内存占用呈线性增长,单实例易成性能瓶颈;
    • 海量用户ID写入压力:每秒百万级请求下,网络IO、序列化开销急剧上升。

    2. 常见技术选型对比分析

    方案精度内存效率读写性能适用场景
    Redis Set精确低(O(n))中等小规模UV统计
    布隆过滤器 (Bloom Filter)有误判率极高极高快速判重前置过滤
    HyperLogLog误差率 ~0.8%极优(约1.5KB/计数器)大规模UV近似统计
    Count-Min Sketch偏向上限估计良好频次分布估计
    Roaring Bitmap精确优于普通Bitmap稀疏ID集合压缩存储

    3. 架构演进路径:从单一到分层混合模型

    1. 初始阶段:使用 Redis Set 按 resource_id 维度存储 user_id 集合;
    2. 第一轮优化:引入本地缓存(Caffeine)+ 布隆过滤器进行客户端预判重;
    3. 第二轮升级:采用 HyperLogLog 替代 Set 实现 UV 近似统计;
    4. 第三阶段:实施 Redis 分片策略,按 resource_id Hash 分布;
    5. 第四阶段:构建流式处理管道,Kafka + Flink 实时聚合写入 HLL;
    6. 最终形态:多级混合架构 —— 实时层(HLL)、准实时层(Flink窗口)、离线层(Spark批处理)协同工作。

    4. 核心组件详解:HyperLogLog 在 UV 统计中的应用

    HyperLogLog(HLL)是一种概率数据结构,通过哈希函数和调和平均估算基数,能够在极小空间内完成亿级去重统计。

    
    // Redis 中操作 HLL 示例
    PFADD uv:video:12345 "user_1001"
    PFADD uv:video:12345 "user_1002"
    PFADD uv:video:12345 "user_1001"  // 重复,不计入
    
    PFCOUNT uv:video:12345  // 返回去重后的UV估值
        

    每个 HLL 结构默认占用约 12KB 内存,可支持上亿级别唯一元素估算,误差率控制在 0.8% 以内,非常适合大规模 UV 场景。

    5. 分片与集群化设计提升横向扩展能力

    当单一 Redis 实例无法承载所有 resource_id 的 HLL 数据时,需引入分片机制:

    • 按 resource_id 哈希取模分配至不同 Redis 节点;
    • 使用一致性哈希减少扩容时的数据迁移成本;
    • 结合 Redis Cluster 或 Codis 中间件实现自动路由;
    • 客户端封装统一的 HLL 操作代理层,屏蔽底层复杂性。

    6. 流式处理增强实时性与可靠性

    为避免突发流量压垮 Redis,可引入消息队列解耦写入压力:

    
    // 用户访问事件发送至 Kafka
    {
      "event_type": "page_view",
      "resource_id": "article_88990",
      "user_id": "u_77231",
      "timestamp": 1712045678901
    }
        

    Flink 消费 Kafka 数据流,按窗口聚合后批量更新各 resource_id 对应的 HLL 计数器,有效降低 Redis 写入频率并保障 Exactly-Once 语义。

    7. 多级缓存与冷热分离策略

    并非所有 resource_id 的访问频率相同。可通过以下方式优化资源利用率:

    • 热资源:驻留 Redis HLL,高频更新;
    • 温资源:定时刷入持久化存储(如 Tair);
    • 冷资源:归档至 Hive 表供离线分析;
    • 使用 LRU/LFU 算法动态识别热点资源。

    8. 混合架构下的误差控制与数据校准

    尽管 HLL 提供了良好的空间效率,但在关键业务中仍需关注误差累积问题:

    • 定期用 Spark 对原始日志做精确 UV 计算,与 HLL 输出比对;
    • 建立偏差修正模型,动态调整 HLL 参数配置;
    • 对超高价值资源(如首页Banner)保留 Redis Set 精确统计作为对照组。

    9. 系统整体架构流程图

    graph TD A[用户访问资源] --> B{本地布隆过滤器} B -- 可能重复 --> C[丢弃或跳过] B -- 新用户 --> D[Kafka消息队列] D --> E[Flink实时计算引擎] E --> F[Redis Cluster - HyperLogLog] F --> G[API服务查询UV] E --> H[写入数据湖 Hive] H --> I[Spark离线校准] I --> J[生成报表 & 偏差分析]

    10. 性能压测与实际落地建议

    在某大型内容平台的实际部署中,该架构支撑了每日超 80 亿次访问事件的 UV 统计:

    • 平均延迟:< 50ms(P99 < 120ms);
    • 内存占用下降 87%(相比原 Redis Set 方案);
    • Redis 写入 QPS 降低 92%(得益于 Flink 批量合并);
    • HLL 估算误差稳定在 ±0.6% 范围内;
    • 支持每分钟新增百万级 resource_id 的动态伸缩能力。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月16日
  • 创建了问题 12月15日