1. 课程导入

欢迎进入《Market Data多源去重与主键设计实战》。在真实行情系统中,我们很少只对接一个数据源:交易所直连、第三方聚合商、内部缓存、历史归档库、甚至人工补录文件,都会同时向同一套行情服务供数。数据一多,问题就不再是“有没有数据”,而是“同一条行情到底以谁为准、如何识别、如何合并、如何追溯”。本课程要解决的,正是这个从多源接入到可信主键落地的完整链路。

学习目标

完成本课程后,你应当具备三项可验证的能力。第一,能够识别多源行情数据中的重复形态,包括完全重复、字段冲突重复、时间窗口重复和跨源语义重复。第二,能够为不同资产类别设计可落地的主键方案,并解释为什么某些场景必须用复合主键、某些场景需要引入代理键。第三,能够把去重规则、优先级策略和主键约束写成可执行的工程配置,而不是停留在文档层面的“原则上”。

  • 理解行情数据的唯一性边界:什么算“同一条”行情。
  • 掌握主键、候选键、代理键、业务键在行情场景中的取舍。
  • 能够设计多源优先级与冲突消解规则,并处理迟到数据。
  • 能够用日志和指标验证去重效果,避免“看起来去重了,实际还在重复”。

核心概念:唯一性不是天然存在的

很多初学者会默认“一条行情记录”天然有唯一标识。但在多源环境下,唯一性是被定义出来的。以逐笔成交为例,交易所可能提供成交编号,但第三方源可能只给时间戳、价格、数量;同一笔成交在A源是 trade_id=88123,在B源可能被拆成两条部分成交,在C源可能因网络重传出现两次。此时,业务键(如 symbol + exchange + trade_id)和代理键(如内部生成的 market_seq)承担不同职责:前者用于跨源对齐,后者用于内部存储与幂等写入。

去重也不是简单的 DISTINCT。它至少包含三个层次:精确去重处理字节级或字段级完全相同的记录;近似去重处理时间戳微差、价格精度差异导致的“几乎相同”;语义去重处理同一市场事件被不同源以不同粒度表达的情况。课程后续章节会分别展开,但导入阶段必须先建立这个分层意识。

关键判断:去重前先问“唯一性由谁定义”。如果唯一性来自交易所,就尊重交易所编号;如果来自内部聚合逻辑,就必须显式设计主键并写入元数据,否则下游无法复现。

整体框架:一条行情从入到用的四道关

本课程按“接入—标准化—去重—主键固化”四道关组织。接入层负责多源连接与缓冲;标准化层统一时间、精度、枚举和字段命名;去重层执行窗口匹配、优先级裁决和冲突标记;主键固化层把最终唯一记录写入存储,并保留血缘信息。下面这张图展示了四层之间的数据流与关键控制点。

接入层 交易所 / 第三方 / 缓存 缓冲与重放 标准化层 时间 / 精度 / 枚举 字段映射与校验 去重层 窗口匹配 / 优先级 冲突标记与裁决 主键固化 代理键 / 血缘 幂等写入 关键控制点:唯一性定义 · 优先级策略 · 迟到数据处理 · 幂等键生成 每个控制点都必须可配置、可观测、可回放 输入:多源原始行情 重复 / 冲突 / 乱序 处理:规则引擎 + 窗口 优先级 / 时间容差 / 字段合并 输出:唯一可信行情 主键 + 血缘 + 质量指标

案例场景:同一笔成交为何出现三次

假设某期货合约在上午10:00:00.123成交,价格3500,数量10手。A源(交易所直连)给出 trade_id=T1001;B源(第三方聚合)因网络延迟在10:00:00.456才到达,且没有trade_id,只有时间戳和价格数量;C源(内部缓存)因重传机制把同一条记录发了两次。如果直接入库,下游会看到三条记录,成交量被放大到30手。

正确的处理路径是:标准化层先把B源时间戳对齐到交易所时钟,并生成候选业务键 symbol + exchange + trade_time + price + volume;去重层在500毫秒窗口内匹配到A源记录,按优先级保留A源,把B源标记为“低优先级重复”;C源的两条记录因携带相同内部序列号,在写入前被幂等键拦截。最终只落一条记录,并保留A源为主、B源为参考的血缘信息。

-- 去重规则配置示例(YAML)
dedup:
  window_ms: 500
  business_key:
    - symbol
    - exchange
    - trade_time
    - price
    - volume
  source_priority:
    - exchange_direct
    - third_party_agg
    - internal_cache
  conflict_policy: keep_highest_priority
  late_data_policy: mark_and_merge
  idempotent_key: "md:{symbol}:{exchange}:{trade_time}:{price}:{volume}"

常见误区与复盘

误区一:用全字段做唯一键。行情字段多且精度不一,全字段做主键会导致“几乎相同”的记录无法去重,且索引膨胀。正确做法是区分业务键与代理键。

误区二:只去重不记录冲突。直接丢弃低优先级记录会丢失数据质量线索。应保留冲突标记和源信息,便于事后审计。

误区三:忽略迟到数据。行情乱序是常态,窗口设置过小会漏去重,过大则增加状态开销。需要根据资产类别和源延迟分布调参。

层次 核心问题 关键产出 常见失败模式
接入层 多源如何稳定进入 缓冲队列、重放日志 源断连导致数据缺口
标准化层 字段与时间如何对齐 统一Schema、时钟校准 精度截断、枚举错配
去重层 什么算重复、谁优先 窗口规则、优先级策略 窗口过大、优先级倒置
主键固化层 如何唯一且可追溯 代理键、血缘、幂等写入 主键冲突、重复写入