2. 逐笔数据特征剖析
逐笔数据(tick-by-tick)与K线、快照数据最大的区别在于:它不是等间隔、等规模的规整序列,而是由市场事件驱动的离散流。要设计一个稳定的缓冲与批量落盘机制,第一步不是选队列、调参数,而是把数据的“本性”摸清楚。本节围绕四个特征展开:到达速率、乱序与重复、时间戳语义、字段结构变化,并说明每一个特征如何直接约束缓冲策略与落盘批次的设计。
一、到达速率:突发性与非均匀性
逐笔数据的到达速率在时间轴上极不均匀。以A股连续竞价为例,开盘集合竞价结束后的前几十秒、整点前后、涨跌停打开瞬间,往往出现每秒数千甚至上万笔的突发;而午间休市前后、行情清淡时段,每秒可能只有几十笔。这种“尖峰—低谷”交替意味着:如果缓冲区按平均速率设计,尖峰时必然积压甚至溢出;如果按峰值设计,低谷时又浪费内存与落盘IO。
工程上的应对是“双阈值+动态批量”。缓冲区设置高水位与低水位:高水位触发提前落盘,防止内存膨胀;低水位用于判断是否可以合并小批次。落盘批次大小不设固定值,而是按“条数或时间窗口先到者触发”的规则,例如 batch_size=2000 或 flush_interval=500ms。这样在突发时以条数为主快速冲刷,在清淡时以时间为主保证时效。示例参数仅为教学假设,实际需按行情源与磁盘能力压测确定。
二、乱序与重复:不能假设“先到即先序”
逐笔数据在传输链路上可能经过多个环节,交易所撮合序号(如逐笔序号、业务序列号)才是判断先后顺序的权威依据,而到达顺序未必与序号一致。多路行情接入、网络重传、多线程消费都会引入乱序。同时,断线重连后的补发、多路源的合并,容易产生重复记录。
因此缓冲层不能简单做“先进先出即落盘”,而应承担轻量的有序化与去重职责:以(市场、证券代码、逐笔序号)作为去重键,在缓冲区内维护一个小窗口做序号连续性检查;对窗口内缺失的序号标记为“待补”,超过容忍窗口仍缺失则记录缺口并放行后续数据,避免因单笔丢失阻塞整条链路。落盘批次设计上,批次内允许存在少量乱序,但批次元数据必须记录该批的序号范围与缺口标记,便于下游按序号重排与对账。
三、时间戳语义:区分“发生时间”与“接收时间”
逐笔数据通常携带两类时间:交易所撮合时间(事件真实发生时刻)和本地接收时间(网关收到时刻)。二者语义完全不同:前者用于业务分析与回测,后者用于链路延迟监控与落盘排序参考。设计中必须明确:落盘分区与批次切分依据哪个时间?
推荐做法是:以交易所时间为业务主时间,用于分区目录(如按交易日、按小时);以本地接收时间作为批次切分的辅助依据和延迟统计字段。切勿用本地时间替代交易所时间做业务排序,否则在跨网关时钟偏差下会产生系统性错序。批次元数据中同时保留两个时间的最小值、最大值,便于事后核对链路延迟分布。
四、字段结构变化:同一市场内的“异构”
逐笔数据并非一张固定表。不同市场(沪、深、北)、不同品种(股票、基金、债券)、不同业务类型(成交、撤单、委托)的字段集合存在差异;交易所也会在版本升级中新增字段。若缓冲区按固定结构体存储,遇到新字段就会截断或报错。
更稳妥的方式是采用“公共字段+扩展字段”的半结构化缓冲:公共字段(代码、序号、时间、价格、数量、方向)强类型存储,扩展字段以键值对或原始报文片段保留。落盘时按“市场+业务类型”分文件或分列族,批次内允许字段并集,缺失字段置空。这样既保证核心字段的查询效率,又不因结构演进导致数据丢失。
五、特征如何共同决定批次参数
把四个特征放在一起看,批次设计就变成一个多约束下的取值问题。举例说明(以下为教学假设场景,非实测):某行情源在开盘突发时峰值约每秒8000笔,清淡时约每秒200笔,单笔平均约120字节。若设定 batch_size=2000、flush_interval=500ms:突发时约250ms即达到条数阈值,单批约240KB,落盘频率约4次/秒;清淡时500ms仅约100笔,单批约12KB,落盘频率2次/秒。这个组合既控制了单批内存上限,又保证了清淡时段的时效性。
再叠加乱序与重复:若容忍窗口设为200个序号,则缓冲区至少需容纳“窗口内待补数据+新到数据”,按峰值估算需预留约2000~4000笔的弹性空间。若字段结构变化导致单笔平均长度上升到200字节,同样的条数阈值会使单批体积增加约67%,此时应同步下调 batch_size 或缩短 flush_interval,避免单批过大导致写放大与恢复时间变长。
六、落到实现上的几个关键约定
第一,缓冲区条目应携带“序号、业务时间、接收时间、去重键、原始载荷”五要素,缺一不可,否则后续无法做有序化与对账。第二,批次切分与分区解耦:切分按条数/时间,分区按业务时间,二者不要混为一谈。第三,字段结构变化要在缓冲入口做一次规范化,把异构报文映射到公共字段,扩展字段原样保留,避免把解析逻辑散落到落盘环节。第四,所有阈值参数应可配置并记录到批次元数据中,便于事后复现某一批数据的生成条件。
归根结底,逐笔数据的四个特征不是四个孤立的知识点,而是一组相互牵制的设计输入:速率决定批量与水位,乱序重复决定去重与窗口,时间戳语义决定排序与分区,字段变化决定存储结构。把这些关系理清,缓冲与落盘的设计才有依据,而不是靠拍脑袋定参数。