3. 快照与增量消息的时序关系
在订单簿一致性校验中,快照与增量消息并不是两份可以随意拼接的数据,它们之间存在严格的时序约束。理解这些约束,是判断“当前本地订单簿是否可信”的前提。本节围绕四个关键点展开:快照生成时刻的语义、增量消息序列号的连续性、乱序与丢失场景的识别,以及一致性校验所依赖的时序窗口与边界条件。
一、快照生成时刻与序列号的锚定关系
交易所或行情源在生成快照时,通常会在快照中携带一个“最后已包含的增量序列号”,记为 lastUpdateId。这个字段的含义是:该快照已经反映了序列号小于等于 lastUpdateId 的全部增量变更。因此,快照不是“某一瞬间的原始状态”,而是“截至某个序列号的状态切片”。
正确的拼接规则是:先加载快照,然后只应用序列号大于 lastUpdateId 的增量消息;对于序列号小于等于 lastUpdateId 的增量,必须丢弃,因为它们已经被快照吸收。若把快照之前的增量重复应用,会造成数量重复累加;若跳过部分增量,则会造成档位缺失。这两种错误在盘口深度上都会表现为“买卖档位数量与真实市场不符”,但成因完全不同,需要靠序列号判断。
实际工程中,快照的获取往往滞后于增量流的订阅。常见做法是:先订阅增量流并缓存,再请求快照,拿到快照后用 lastUpdateId 在缓存中定位起点,丢弃过期增量,从第一个大于该值的序列号开始回放。这样能保证“快照 + 增量”在序列号维度上无缝衔接。
二、增量消息序列号的连续性判断
增量消息通常带有单调递增的序列号,记为 U(起始序列号)和 u(结束序列号),一条消息可能覆盖一段区间。判断连续性的核心条件是:下一条消息的 U 应等于上一条消息的 u + 1。若 U > 上一条 u + 1,说明中间有消息丢失;若 U ≤ 上一条 u,说明出现重复或乱序。
需要注意,不同交易所对序列号区间的定义不完全一致,有的消息只带单一序列号,有的带区间。校验逻辑必须按数据源的实际语义实现,不能想当然地套用“加一”规则。下面给出一个简化的连续性检查示例(示例参数,非实测数据):
上一条消息: U=1001, u=1005
下一条消息: U=1006, u=1010 → 连续,正常应用
下一条消息: U=1008, u=1012 → U > 1005+1,判定丢失 1006~1007
下一条消息: U=1003, u=1007 → U ≤ 1005,判定重复/乱序,需按规则处理
三、乱序与丢失场景的识别与处理
网络传输和消息队列都可能造成乱序。乱序的典型特征是:后到达的消息序列号小于已处理的最大序列号。处理策略取决于业务对实时性的要求。若允许短暂缓冲,可维护一个小窗口,将乱序消息暂存,等待缺失序列号补齐后再按序应用;若不允许缓冲,则一旦检测到序列号回退,应标记本地订单簿为“不可信”,触发重新拉取快照。
丢失比乱序更危险,因为它不会自动恢复。丢失的判定依据是序列号出现跳跃。此时本地订单簿已经缺少一段变更,继续在其上叠加后续增量只会让误差累积。正确做法是立即停止应用增量,重新获取快照并从新的 lastUpdateId 重新锚定。切忌“跳过丢失段继续跑”,那会让后续所有校验失去意义。
四、一致性校验的时序窗口与边界条件
一致性校验不是每来一条消息就全量比对,而是围绕一个“时序窗口”进行。窗口的起点是快照的 lastUpdateId,终点是当前已应用的最大序列号。在这个窗口内,本地订单簿应当与数据源在相同序列号处的状态一致。校验时通常选取若干关键档位(如买一卖一、前五档)比对价格与数量,而不是比对全部档位,以控制开销。
边界条件最容易出错的有三处。第一,快照与增量衔接的“第一条消息”:必须确认其 U ≤ lastUpdateId + 1 ≤ u,即快照的终点落在第一条增量覆盖的区间内,否则说明快照与增量流之间存在缺口。第二,窗口为空的情况:若快照刚加载、尚未应用任何增量,此时不应判定为“不一致”,而应标记为“待校验”。第三,序列号回绕或重置:部分数据源在维护后会重置序列号,此时旧窗口失效,必须重新锚定,不能把重置误判为丢失。
把上述规则落到实现上,可以维护三个状态量:snapshotId(快照锚点)、lastApplied(已应用的最大序列号)、trusted(本地订单簿是否可信)。每收到一条增量,先做连续性判断,再决定应用、缓冲还是触发重同步。校验时以 snapshotId 为起点、lastApplied 为终点构造窗口,窗口内比对关键档位;窗口一旦断裂,trusted 置为假,直到新快照重新锚定。
最后强调一个容易被忽视的细节:快照的“生成时刻”与“接收时刻”不同。快照在数据源侧生成后,经过网络到达本地,这期间增量仍在产生。因此不能用本地接收快照的时间去推断序列号,只能以快照内携带的 lastUpdateId 为准。把生成时刻、序列号、接收时刻三者混为一谈,是时序校验中最隐蔽的错误来源。