3. 多源数据挑战

在真实行情系统中,同一只股票的价格往往同时来自交易所直连、券商柜台、第三方数据商、网页抓取接口等多个通道。多源接入的初衷是提升覆盖率与容错能力,但随之而来的是一组高度耦合的数据治理难题:不同源对同一事实的描述方式不一致,导致“看起来都是行情,却无法直接合并”。本节聚焦四类核心挑战——数据源差异、时间戳偏差、字段映射、重复与冲突,并给出可落地的识别与处理思路。

一、核心原理:多源差异的本质是“语义与时钟不同步”

多源数据之所以难以去重,根本原因不在数据本身,而在于三个层面的不同步:语义层(字段含义、单位、精度不同)、时间层(采集时刻、事件时刻、入库时刻不同)、标识层(同一标的在不同源中的代码、名称、市场后缀不同)。去重与主键设计,本质上是在这三层之间建立可对齐的“锚点”。

例如,A源返回 last_price 单位为元、保留两位小数;B源返回 price 单位为分、整数存储。若不做单位归一,同一笔成交会被判为两条不同记录。再如,A源时间戳为交易所撮合时间,B源为本地接收时间,两者相差数十毫秒,若直接按时间戳去重,会误删真实的不同快照。

多源行情对齐的三层锚点 语义层 字段名 / 单位 / 精度 last_price ↔ price 元 ↔ 分 时间层 事件时间 / 采集时间 交易所撮合 ↔ 本地接收 毫秒偏差 标识层 代码 / 市场后缀 / 名称 600519 ↔ 600519.SH SH600519 统一主键锚点 symbol + event_time + source_priority 对齐后:同一事实可判定为重复,不同事实可保留为多快照

二、方法流程:从字段映射到冲突裁决

处理多源数据,建议按“先对齐、再判重、后裁决”的顺序推进,避免在未归一的情况下直接去重。

  1. 字段映射:建立源字段到标准字段的映射表,明确单位换算与精度规则。例如 price 分转元需除以100,vol 手转股需乘以100。
  2. 时间对齐:区分事件时间与采集时间,统一到同一时区与精度(如毫秒)。对采集时间偏差较大的源,优先使用事件时间作为主键组成部分。
  3. 标识归一:将各源代码统一为标准 symbol,如 600519.SH,并保留原始代码作为溯源字段。
  4. 判重:以“标准symbol + 事件时间 + 业务类型”为候选主键,识别同一事实的多条记录。
  5. 冲突裁决:当同一主键下字段值不一致时,按源优先级、时间新鲜度或多数投票规则选择保留值。

关键原则:去重不是简单删除,而是“识别同一事实并保留最优表达”。主键设计要能同时支撑判重与溯源,因此建议保留 source 与 ingest_time 作为辅助字段。

三、案例场景:同一笔成交的多源记录

假设某时刻贵州茅台发生一笔成交,三个源返回如下:

源 代码 价格 时间戳 类型
A(交易所直连) 600519.SH 1680.00 元 10:00:00.120 事件时间
B(第三方) SH600519 168000 分 10:00:00.185 采集时间
C(网页抓取) 600519 1680.0 元 10:00:01.000 采集时间

若直接按原始字段去重,三条记录代码、单位、时间均不同,会被误判为三笔独立成交。正确做法是:先归一代码为 600519.SH,价格统一为元,时间统一到事件时间并允许±200ms容差,再以 symbol + event_time_bucket + trade_type 判重。此时A与B可合并为同一事实,C因时间偏差较大需结合业务判断是否保留为独立快照。

-- 归一与判重示例
SELECT
  standard_symbol,
  DATE_TRUNC('second', event_time) AS time_bucket,
  trade_type,
  COUNT(*) AS source_count,
  MAX_BY(price, source_priority) AS final_price
FROM normalized_market_data
GROUP BY standard_symbol, time_bucket, trade_type;

四、误区与复盘

常见误区一:用采集时间做主键。采集时间受网络与调度影响,同一事件在不同源可能相差数百毫秒甚至数秒,直接做主键会导致大量伪重复。

常见误区二:忽略单位与精度。价格分与元、成交量手与股未归一,去重结果必然失真。

常见误区三:冲突时简单取最新。最新采集的记录未必最准确,应结合源优先级与业务规则综合裁决。

复盘时建议建立“多源差异清单”,逐项记录每个源的字段含义、时间语义、代码格式与已知偏差,并定期用抽样比对验证映射规则是否仍然有效。只有把差异显性化、规则化,去重与主键设计才能从“救火”走向“可运营”。