CodeWalk

DAU(日活跃用户)的精确与近似统计方法对比

作者:屠龙少年 · 2026-05-30 12:55

在大数据场景下统计日活跃用户(DAU),请对比精确去重(Count Distinct)和近似去重(HyperLogLog/BitMap)的实现方法、性能差异和误差控制。给出一个Flink SQL计算实时DAU的完整示例(使用HyperLogLog),以及一个Spark SQL计算离线T+1 DAU的精确方案。讨论如何保证跨天去重的准确性。

回答

屠龙少年

DAU统计方法对比:

1. 精确去重(Count Distinct)

-- Spark SQL T+1精确DAU
SELECT
date,
COUNT(DISTINCT user_id) AS dau
FROM dwd.event_logs
WHERE dt = '2025-05-25'
  AND event = 'app_start'
GROUP BY date;
  • 特点:100%准确,但需要大量Shuffle和内存
  • 性能:大表(10亿+)可能OOM
  • 优化:使用approx_count_distinct或先去重再计数

2. 近似去重(HyperLogLog)

-- Flink SQL实时DAU(HyperLogLog)
SELECT 
TUMBLE_START(event_time, INTERVAL '1' DAY) AS window_start,
COUNT(DISTINCT user_id) AS dau_approx  -- Flink自动优化为HyperLogLog
FROM events
WHERE event_name = 'app_start'
GROUP BY TUMBLE(event_time, INTERVAL '1' DAY);
  • 特点:内存极小(12KB),误差<1%左右
  • 性能:O(1)固定内存,几乎无Shuffle

3. BitMap精确去重

-- Doris BitMap精确DAU
SELECT
BITMAP_COUNT(user_bitmap) AS dau
FROM (
 SELECT BITMAP_UNION(to_bitmap(user_id)) AS user_bitmap
 FROM events
 WHERE dt = '2025-05-25' AND event = 'app_start'
);
  • 特点:精确,内存取决于用户基数
  • 性能:BITMAP_UNION可并行计算

4. 方法对比: | 方法 | 准确性 | 内存 | 速度 | 适合场景 | |------|--------|------|------|---------| | COUNT DISTINCT | 100% | O(N) | 慢 | T+1精确报表 | | HyperLogLog | ~99% | 12KB | 极快 | 实时大屏/近似值 | | BitMap(RoaringBitmap)| 100% | O(UV) | 中 | Doris/Presto预计算 |

5. 跨天去重考虑

  • 滚动DAU(7日/30日):使用BitMap或HyperLogLog合并多天数据
  • BITMAP_OR(day1_bitmap, day2_bitmap, ...)计算多日累计活跃
  • HyperLogLog支持hll_union()合并多个hll对象

6. Flink SQL实时DAU完整示例

CREATE VIEW dau_view AS
SELECT
  window_start,
  COUNT(DISTINCT user_id) AS dau
FROM TABLE(TUMBLE(TABLE events, DESCRIPTOR(event_time), INTERVAL '1' DAY))
WHERE event_name = 'app_start'
GROUP BY window_start;