4. 数据补录核心概念

断点续传解决的是“连接中断后如何接着传”,而数据补录解决的是“已经缺失的数据如何找回来、补进去、且不污染已有数据”。在行情采集系统中,补录不是简单的重跑任务,它涉及缺口识别、时间边界、回补方式、数据对齐与去重五个相互咬合的核心概念。理解它们之间的关系,才能设计出可靠的补录链路。

一、补录场景:什么时候必须补录

补录并非只在“采集失败”时才发生。实际生产环境中,以下场景都会触发补录需求:

  • 网络中断或超时:采集进程与行情源断开,期间数据未落库。
  • 源端延迟发布:行情源在收盘后修正或补发某段时间的数据。
  • 服务重启或宕机:采集节点崩溃,内存中未持久化的数据丢失。
  • 解析或校验失败:数据已到达但格式异常被丢弃,需要重新拉取。
  • 历史数据迁移:新系统上线,需要从旧库或文件回填历史区间。

不同场景对应不同的补录优先级和窗口策略。例如网络中断通常只需补最近几分钟,而历史迁移可能跨越数月,必须分批处理。

二、缺失数据识别:先找到缺口,再谈补录

补录的第一步不是“拉数据”,而是“确认缺了什么”。行情数据通常具有明确的时间序列特征,识别缺口的核心方法是时间轴比对:以预期的时间粒度(如1秒、1分钟)生成完整时间序列,与已落库数据的时间戳集合做差集。

-- 以分钟线为例,识别缺失时间窗口
SELECT expected.ts
FROM generate_series(
  '2024-06-01 09:30:00'::timestamp,
  '2024-06-01 11:30:00'::timestamp,
  '1 minute'
) AS expected(ts)
LEFT JOIN market_bars b ON b.ts = expected.ts
WHERE b.ts IS NULL
ORDER BY expected.ts;

除了时间戳缺失,还要识别记录数异常和字段空值。例如某分钟应有200条逐笔,实际只有50条,说明部分数据丢失;某条记录的成交量字段为NULL,说明解析不完整。识别结果应输出为“缺口清单”,包含起始时间、结束时间、缺失类型和影响范围。

建议将缺口识别做成定时巡检任务,而不是等到人工发现。巡检频率可与采集频率一致,发现缺口立即写入补录队列。

三、时间窗口:补录的边界与粒度

时间窗口决定了补录请求的起止范围。窗口划分过粗,会拉取大量已存在的数据,浪费带宽和计算资源;窗口划分过细,会产生大量小请求,增加源端压力。常见策略有三种:

策略 窗口划分方式 适用场景 注意事项
固定窗口 按固定时长(如5分钟)切分 缺口分散、源端支持批量查询 窗口边界需对齐交易日历
动态窗口 根据缺口连续段自动合并 缺口集中、连续缺失 单窗口不宜超过源端限制
滑动窗口 以当前时间为终点向前回溯 实时补录、最近数据修复 需设置最大回溯深度

无论采用哪种策略,都必须处理交易日历问题。非交易时段不应生成补录窗口,否则会产生大量无效请求。同时,窗口边界要与源端的时间语义对齐,避免因时区或毫秒截断导致重复拉取。

四、回补策略:怎么补、补到什么程度

回补策略决定了补录请求的发起方式和失败处理逻辑。常见策略包括:

  1. 全量回补:对缺口窗口重新拉取全部数据,简单可靠,但可能覆盖已有正确数据。
  2. 增量回补:只拉取缺失的时间点或记录,依赖源端支持精确查询。
  3. 优先级回补:按业务重要性排序,先补关键品种或关键时段。
  4. 退避重试:补录失败后按指数退避重试,避免频繁冲击源端。
全量回补时如果直接覆盖,可能把已修正的数据再次覆盖为旧值。建议采用“先比对、后写入”的方式,或写入临时表再合并。

回补过程中要记录每次请求的批次号、窗口范围、源端响应状态和写入结果,形成可追溯的补录日志。这样在出现数据异常时,可以快速定位是哪次补录引入的问题。

五、数据对齐与去重:补录的最后一道防线

补录数据写入前,必须与已有数据做对齐和去重。对齐是指时间戳、品种代码、字段结构要与主表保持一致;去重是指同一时间点、同一品种的记录只能保留一条。

去重的核心是确定唯一键。行情数据通常以“品种代码 + 时间戳 + 周期”作为唯一键。写入时可采用以下方式:

-- 使用唯一索引 + ON CONFLICT 实现幂等写入
INSERT INTO market_bars (symbol, ts, open, high, low, close, volume)
VALUES ('IF2406', '2024-06-01 09:31:00', 3600.2, 3601.0, 3599.8, 3600.5, 1200)
ON CONFLICT (symbol, ts) DO UPDATE
SET open = EXCLUDED.open,
    high = EXCLUDED.high,
    low = EXCLUDED.low,
    close = EXCLUDED.close,
    volume = EXCLUDED.volume
WHERE market_bars.updated_at < EXCLUDED.updated_at;

对于逐笔数据,唯一键可能更复杂,需要结合成交编号或序列号。去重时还要注意数据版本问题:源端可能对同一时间点发布修正值,此时应保留最新版本而非简单丢弃。

数据补录核心链路 缺失数据识别 时间轴比对 记录数/空值检查 时间窗口划分 固定/动态/滑动 交易日历对齐 回补策略 全量/增量/优先级 退避重试 对齐与去重 唯一键约束 版本保留 补录数据流:缺口清单 → 窗口请求 → 源端拉取 → 临时落库 → 对齐去重 → 合并主表 每步记录批次号、窗口范围、响应状态、写入结果 关键原则一 先识别缺口,再发起补录 避免盲目全量重跑 关键原则二 窗口边界对齐交易日历 非交易时段不生成请求 关键原则三 唯一键 + 版本控制 保证幂等写入与去重

六、常见误区与复盘要点

在实际补录实践中,以下误区最容易导致数据质量问题:

  • 只补不验:补录完成后没有校验记录数和时间连续性,缺口依然存在。
  • 窗口重叠:多个补录任务窗口重叠,导致同一时间段被重复拉取和写入。
  • 忽略时区:源端使用UTC,本地使用北京时间,窗口边界偏移导致漏补或重补。
  • 覆盖修正值:全量覆盖时把源端已修正的数据覆盖回旧值。
  • 无批次追踪:出问题后无法定位是哪次补录引入的异常数据。
补录链路的设计目标可以概括为:找得准、补得全、写得净、查得到。找得准依赖缺口识别,补得全依赖窗口与策略,写得净依赖对齐去重,查得到依赖批次日志。四者缺一不可。

复盘时建议重点关注三个指标:补录成功率、补录后缺口残留率、重复数据发生率。这三个指标能直接反映补录链路是否健康。如果补录成功率低,检查源端限流和重试策略;如果缺口残留率高,检查窗口划分和交易日历;如果重复数据发生率高,检查唯一键设计和写入冲突处理。