--- id: backend.data-pipeline.reliability title: 数据管道基础:批处理与流处理、增量同步、限流重试、幂等、数据质量和告警 type: lesson domain: backend stack: [data-pipeline] tags: [ETL, 批处理, 流处理, 增量同步, 水位线, 限流, 重试, 幂等, 死信, 数据质量, 告警] level: intermediate prerequisites: [backend.postgresql.index-tx-mvcc, backend.mq.reliability] est_hours: 3 related: [backend.data-pipeline.scenario.third-party-sync, backend.data-pipeline.interview, backend.elasticsearch.scenario.kb-search-sync] week: [14] order: 1 --- # 数据管道基础:批处理与流处理、增量同步、限流重试、幂等、数据质量和告警 > BOSS直聘 2026-10-23 的调研中,部分 AI 应用岗位要求“数据处理 / 数据管道”能力:知识库文档的采集清洗、第三方平台数据对接、报表数据准备。这里讲后端工程师需要掌握的基础,不涉及大数据平台。 ## 解释说明 ### 批处理和流处理 | | 批处理 | 流处理 | | --- | --- | --- | | 触发 | 定时(每小时 / 每天) | 事件到达即处理 | | 延迟 | 分钟到小时 | 秒级 | | 实现 | 定时任务 + SQL / Python | 消息队列(Kafka / RabbitMQ)+ 消费者,或 CDC | | 适合 | 报表、对账、全量重建 | 搜索同步、实时告警、实时推荐 | 很多系统两者结合:流处理保证实时,批处理定期对账兜底。 ### 增量同步的关键 1. **水位线**:记录上次同步到哪里(时间戳 + 主键打破平局),失败后从水位线继续。**水位线只在本批数据成功落库后推进**。 2. **限流**:第三方接口有调用频率限制,用令牌桶控制;收到 429 时遵守 `Retry-After`。 3. **重试**:区分可重试错误(超时、5xx、429)和不可重试错误(401、参数错误);指数退避 + 抖动(避免大量客户端同时重试),有上限。 4. **幂等**:重复运行结果一致——按主键 upsert,并且比较版本号 / 更新时间,旧数据不能覆盖新数据。 5. **坏数据**:单条坏数据进入死信表,不阻塞整批;坏数据比例超过阈值说明上游出问题了,应停止并告警。 6. **数据质量检查**:行数波动、空值率、取值范围、唯一性、与源头的对账。 7. **可观测**:每次运行记录拉取数、写入数、死信数、耗时;失败和质量异常告警。 ### 常用工具(了解) 定时调度:cron、Celery Beat、APScheduler;编排:Airflow、Dagster、Prefect;CDC:Debezium;数据质量:Great Expectations 一类工具。小团队先用“定时任务 + 本课的这些原则”就够了。 ## 案例 每 10 分钟从电商平台开放接口同步订单到自己的数据库,供 AI 报表 Agent 查询:接口每秒限 50 次、偶尔超时;有时返回状态值异常的数据;同步中断后要能续跑;不能产生重复订单。 ## 代码示例 [examples/incremental-sync](examples/incremental-sync/)(只用标准库,`python pipeline.py` 运行,`test_pipeline.py` 6 个测试通过): ```text 第一次:拉取 251,写入 250,死信 1,接口调用 5 次(含 2 次失败重试) 第二次(没有新数据):拉取 0 orders 表 250 行,水位线 ('2026-10-23T11:00:00', 'bad1') ``` 幂等且防止旧版本覆盖: ```sql INSERT INTO orders (id, amount_cents, status, updated_at) VALUES (:id, :amount_cents, :status, :updated_at) ON CONFLICT(id) DO UPDATE SET amount_cents = excluded.amount_cents, status = excluded.status, updated_at = excluded.updated_at WHERE excluded.updated_at > orders.updated_at ``` 重试(指数退避 + 全抖动,遵守 Retry-After): ```python def with_retry(fn, *, attempts=5, base=0.2, cap=5.0, sleep=time.sleep, rng=None): rng = rng or random.Random() for i in range(attempts): try: return fn() except RetryableError as e: if i == attempts - 1: raise delay = e.retry_after if e.retry_after is not None else rng.uniform(0, min(cap, base * 2 ** i)) sleep(delay) ``` 质量检查不通过时整批回滚、水位线不前进并告警(见 `test_quality_gate_rolls_back_and_alerts`)。 ## 易错点 - 先推进水位线再写数据:中途失败就丢数据。 - 只用时间戳做水位线:同一秒内有多条数据时会漏或重复;要加主键打破平局。 - **迟到数据**:来源系统的事务晚提交,`updated_at` 比水位线还早,被永远跳过。办法:每次往回多拉一个重叠窗口(例如 5 分钟),依赖幂等写入去重;再加定期对账。 - 对所有错误都重试,包括 401,结果被对方封禁。 - 没有抖动的重试,故障恢复瞬间所有任务一起打过去。 - 坏数据直接让整个任务失败,或者相反——静默丢弃没有任何记录。 ## 面试常问 - 怎么设计一个可以断点续传的增量同步任务? - 怎么保证同步任务幂等? - 第三方接口限流和不稳定怎么处理? - 怎么发现数据质量问题?