跳到主要内容

数据重复排查

场景描述

运营发现报表中某些订单被统计了两次,导致 GMV 虚高。需要定位重复原因并修复。

常见重复原因

原因说明排查方法
数据源重复上游日志重复发送检查原始日志 unique key
ETL 重跑任务重跑未做幂等检查任务日志和分区数据
JOIN 膨胀一对多 JOIN 产生膨胀检查 JOIN 结果行数
CDC 重复投递Kafka 消息重复消费检查 offset 和消息 ID
缓慢变化维维度表有多条历史记录检查维度表是否去重

排查步骤

1. 定位重复记录

-- 找出重复的订单
SELECT order_id, COUNT(*) AS cnt
FROM dwd_order_detail
WHERE dt = '2024-01-15'
GROUP BY order_id
HAVING COUNT(*) > 1
ORDER BY cnt DESC
LIMIT 20;

2. 分析重复原因

-- 对比重复记录的完整字段,找出差异
SELECT *
FROM dwd_order_detail
WHERE order_id IN ('ORD_001', 'ORD_002')
ORDER BY order_id, etl_time;

-- 如果两条记录内容完全一致 → ETL 重跑或数据源重发
-- 如果金额不同 → 订单状态变更产生多条记录

3. JOIN 膨胀检查

-- 检查 JOIN 前后行数
SELECT COUNT(*) FROM order_fact; -- 100 万
SELECT COUNT(*) FROM order_fact a
JOIN product_sku b ON a.sku_id = b.sku_id; -- 如果 > 100 万,说明膨胀了

-- 原因:product_sku 表中 sku_id 不唯一(有多个版本/颜色)
-- 修复:JOIN 前先去重
SELECT COUNT(*) FROM order_fact a
JOIN (SELECT DISTINCT sku_id, sku_name FROM product_sku) b
ON a.sku_id = b.sku_id; -- 应该 = 100 万

修复方案

去重策略

-- 方案一:ROW_NUMBER 去重(保留最新一条)
SELECT * FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY update_time DESC) AS rn
FROM dwd_order_detail
WHERE dt = '2024-01-15'
) t WHERE rn = 1;

-- 方案二:GROUP BY 聚合去重
SELECT order_id,
MAX(amount) AS amount,
MAX(update_time) AS update_time
FROM dwd_order_detail
WHERE dt = '2024-01-15'
GROUP BY order_id;

幂等设计

-- ETL 任务幂等写入:INSERT OVERWRITE 而非 INSERT INTO
INSERT OVERWRITE TABLE dwd_order_detail PARTITION (dt='2024-01-15')
SELECT DISTINCT order_id, user_id, amount, created_at
FROM ods_order_log
WHERE dt = '2024-01-15';

常见面试问题

Q1: 如何检测数据仓库中的重复数据?

答案

  1. 主键检查SELECT pk, COUNT(*) GROUP BY pk HAVING COUNT(*) > 1
  2. dbt testunique 约束测试
  3. 监控规则:每日自动检查核心表主键唯一性
  4. 行数对比:ETL 前后行数对比,膨胀超过阈值告警

答案

  • Exactly-Once 语义:Flink Checkpoint + Kafka 事务
  • 业务层去重:用 Redis 或数据库主键约束做幂等
  • 最终兜底:下游查询时用 ROW_NUMBER() 去重

相关链接