2. 逐笔数据丢包检测机制

逐笔数据(逐笔委托、逐笔成交)是行情链路中价值最高、也最脆弱的一类数据。与快照行情"每秒几帧、丢一帧还能等下一帧"不同,逐笔数据是连续流,任何一条记录的缺失都可能让下游的策略回测、实时风控或撮合校验出现偏差。因此,排障的第一步不是"补",而是"发现"——只有先可靠地检测到丢包,补录才有明确的目标和验收标准。本节围绕三种互补的检测手段展开:序列号连续性检测、时间戳间隙检测、行情快照交叉比对,并给出监控告警的落地配置思路。

一、序列号检测:最直接、最可靠的连续性判据

交易所的逐笔数据通常带有两类序号:一类是通道内序号(如某交易所逐笔委托的 AppSeqNum、逐笔成交的 BizIndex),在单一通道内严格递增;另一类是全局序号或撮合序号,用于跨通道排序。检测的核心逻辑非常朴素:对同一通道、同一数据类型,后一条记录的序号应当等于前一条加 1(或按协议规定的步长递增)。一旦出现跳变,缺口大小就是丢失条数。

实际实现时要注意三个细节。第一,序号作用域必须对齐:不同通道、不同数据类型(委托与成交)各自独立编号,不能混在一个计数器里比较,否则会产生大量误报。第二,要区分"跳变"与"重置":交易日切换、通道重连、交易所侧序号回绕都会造成序号回退,这类情况应识别为重置而非丢包,处理方式是重置基线而不是告警。第三,要记录缺口区间:不仅要知道"丢了 5 条",还要知道"从 10231 到 10235 丢了 5 条",这个区间正是后续补录的查询条件。

# 序列号连续性检测伪代码(按 通道+数据类型 分组)
for each record in stream:
    key = (record.channel_id, record.data_type)
    last = baseline.get(key)
    if last is None:
        baseline[key] = record.seq          # 首条,建立基线
    elif record.seq == last + 1:
        baseline[key] = record.seq          # 连续,正常推进
    elif record.seq <= last:
        baseline[key] = record.seq          # 回退,判定为重置,不告警
        log_reset(key, last, record.seq)
    else:
        gap = record.seq - last - 1
        emit_gap(key, start=last+1, end=record.seq-1, count=gap)
        baseline[key] = record.seq

二、时间戳检测:捕捉"序号连续但内容异常"的隐性丢失

序列号并非万能。有些链路的中间件在转发时会重排或合并消息,导致序号看似连续、实际内容已经缺失;还有些场景(如部分 Level-2 逐笔合并行情)本身不提供严格连续的序号。这时时间戳就成为第二道防线。逐笔记录通常携带交易所时间戳(精确到毫秒甚至微秒),同一通道内相邻记录的时间差应落在合理区间内。若出现明显超出正常分布的时间间隙,就提示该区间可能存在丢失或严重延迟。

时间戳检测的难点在于阈值设定。阈值太松会漏报,太紧会误报。工程上的做法是先采集一段正常交易时段(例如连续 3 个交易日)的相邻时间差分布,取 P99.9 分位数作为基线阈值,再乘以一个安全系数(如 1.5~2 倍)。例如某通道逐笔成交的相邻时间差 P99.9 为 80ms,则可把 150ms 设为告警阈值。同时要区分时段:开盘集合竞价、午间休市前后、收盘瞬间的流量特征差异极大,应按交易阶段分别设定阈值,而不是全天一个数。

时间戳检测更适合作为"辅助发现"手段:它告诉你"这里可能有问题",但不能精确给出丢失条数。真正定位缺口区间,仍要回到序列号或快照比对。

三、行情快照交叉比对:用另一路数据反推逐笔完整性

快照行情与逐笔数据来自同一撮合源,二者存在天然的对应关系:某只股票在时刻 T 的快照中,累计成交量、累计成交笔数、最新价、买卖盘口,都是由截至 T 的全部逐笔成交聚合而来。因此可以用快照做"对账":把本地收到的逐笔成交按股票聚合,计算累计成交量与累计笔数,再与同一时刻快照中的对应字段比较。若逐笔聚合值小于快照值,差额就是疑似丢失的量。

举个教学示例(数据为合理假设,非实测):某股票在 10:00:00.000 的快照显示累计成交量 1,250,000 股、累计成交笔数 860 笔;本地逐笔成交聚合得到 1,248,600 股、859 笔。差额为 1,400 股、1 笔,说明在上一快照到本快照之间至少丢失了 1 笔成交。此时再结合序列号缺口区间,就能把"丢了 1 笔"精确定位到具体序号段。需要注意的是,快照与逐笔之间存在固有的到达时序差,比对时必须做时间对齐(通常以快照时间戳为准,取该时刻之前的逐笔聚合),否则会把"还没到"误判成"丢了"。

逐笔数据丢包三重检测与告警链路 逐笔数据流 委托 / 成交 行情快照流 累计量 / 累计笔数 ① 序列号连续性检测 按 通道+类型 分组 seq ≠ last+1 → 缺口 ② 时间戳间隙检测 相邻时间差 > 阈值 按交易阶段分档 ③ 快照交叉比对 逐笔聚合 vs 快照累计 差额 → 疑似丢失量 缺口证据汇聚 通道 / 类型 / 序号区间 丢失条数 / 时间窗 置信度(单源/多源) 告警判定 单条缺口 → 记录 连续缺口 ≥ N → 告警 多源同时命中 → 升级 序号重置 → 抑制 输出补录工单 判定优先级: 序列号缺口(精确、可定位) > 快照比对(可量化、可交叉验证) > 时间戳间隙(辅助发现、易误报) 误报抑制: 交易日切换、通道重连、序号回绕 → 重置基线;快照比对须做时间对齐,避免"未到达"误判为"丢失" 输出物: 缺口区间(起始序号、结束序号、条数、通道、数据类型、时间窗)→ 直接作为补录查询条件

四、监控告警配置实战

检测逻辑写进代码只是第一步,真正决定排障效率的是告警配置是否"准"和"可行动"。一条好的丢包告警应当包含:哪个通道、哪种数据类型、缺口起止序号、丢失条数、发生时间窗、以及是否被多源证据交叉确认。下面给出一份可直接参考的告警规则配置示例(参数为教学示例,需按实际链路调整)。

# 逐笔丢包告警规则示例(YAML 风格,参数为示例值)
rules:
  - name: tick_seq_gap
    source: tick_stream
    group_by: [channel_id, data_type]
    condition: seq_gap_count >= 1
    severity: info            # 单条缺口先记录,不打扰值班
    action: record_gap

  - name: tick_seq_gap_burst
    source: tick_stream
    group_by: [channel_id, data_type]
    condition: seq_gap_count >= 20 within 5s
    severity: warning
    action: alert_and_create_ticket

  - name: tick_ts_gap
    source: tick_stream
    group_by: [channel_id, data_type, trading_phase]
    condition: inter_arrival_ms > phase_threshold_ms
    severity: warning
    action: alert

  - name: tick_snapshot_mismatch
    source: snapshot_vs_tick
    group_by: [symbol]
    condition: snapshot_cum_volume - tick_cum_volume > 0
    severity: critical        # 快照对账不平,通常意味着真实丢失
    action: alert_and_create_ticket

  - name: seq_reset_suppress
    source: tick_stream
    condition: seq <= last_seq
    action: reset_baseline      # 重置不告警,仅记录

配置中有几个关键取舍值得说明。分级而非一刀切:单条缺口在高速行情中并不罕见(多为链路抖动或短暂乱序),若每条都告警会造成告警疲劳,因此设为 info 级仅记录;只有连续、成批的缺口才升级为 warning 并生成补录工单。快照对账设为 critical:因为快照与逐笔同源,对账不平几乎可以确认是真实丢失,误报率低,值得立即响应。重置抑制必须显式配置:否则每个交易日开盘、每次通道重连都会触发大量假告警,反而掩盖真实问题。

告警落地后,还要建立"告警—定位—补录—复核"的闭环。收到 warning 或 critical 告警时,值班人员应第一时间从告警内容中取出缺口区间,向交易所或上游行情源发起补录请求;补录完成后,用同一套检测逻辑对补录数据重新跑一遍序列号连续性和快照对账,确认缺口已闭合、对账已平,才算真正闭环。若补录后仍不平,则说明缺口不止一处,或补录数据本身也有问题,需要回到链路层面继续排查。这套机制的价值在于:它把"感觉数据不对"这种模糊判断,转化成了可量化、可验证、可复现的工程流程。

检测阈值和告警规则不是一次配置就永久有效的。交易所协议升级、通道扩容、交易时段调整都可能改变流量特征,建议至少每季度用最近一段正常数据回放一次检测逻辑,校准阈值并复核误报率与漏报率。