3. 基于时间分片的归档目录规划
逐笔行情数据的特点是“量大、连续、按时间自然增长”。如果所有归档文件都堆在一个目录里,或者只按品种简单分文件夹,随着时间推移,单目录文件数会迅速膨胀,检索时既难以定位,也容易拖慢文件系统遍历。基于时间分片的归档目录规划,核心思路是:把时间作为第一维度的组织轴,用目录层级表达时间粒度,用文件命名表达分片边界,用元数据文件表达可检索属性。这样归档结构既能随交易日自然扩展,又能让“找某天某小时某品种的数据”变成一次确定性的路径拼接,而不是全盘扫描。
目录层级:以交易日为主轴,小时为可选细分
推荐的基础层级为“根目录 / 数据域 / 年 / 交易日 / 小时(可选)”。其中“数据域”用于区分不同来源或不同处理阶段,例如原始逐笔、清洗后逐笔、特征快照等;年目录用于控制单层子目录数量;交易日目录是归档的最小完整单元,因为逐笔行情天然以交易日为边界,跨日切分会产生大量边界处理逻辑。小时目录只在单日数据量特别大、或需要按小时增量落盘时启用,属于可选的二级分片。
一个可直接采用的目录结构示例如下(示例参数,非实测):
archive/
tick_raw/ # 数据域:原始逐笔
2024/
20240115/ # 交易日 YYYYMMDD
09/ # 小时 HH(可选)
tick_raw_20240115_09_IF2401.csv.gz
tick_raw_20240115_09_IF2401.meta.json
10/
tick_raw_20240115_10_IF2401.csv.gz
tick_raw_20240115_10_IF2401.meta.json
20240116/
...
tick_clean/ # 数据域:清洗后逐笔
2024/
20240115/
...
这里有一个关键取舍:品种是否进入目录层级。如果品种数量少且固定,可以把品种放进文件名而不建目录,避免目录过深;如果品种多、且经常按品种批量检索,可以在交易日下再加一层品种目录。判断依据是“检索主路径”——若日常查询以“某天全市场”为主,品种放文件名;若以“某品种某段时间”为主,品种建目录。不要同时把品种和时间都做成多层目录,否则路径过深、维护成本上升。
文件命名规范:可解析、可排序、可去重
文件名应满足三个要求:机器可解析、字典序即时间序、包含分片边界信息。推荐格式为:
{数据域}_{交易日}_{小时}_{品种}_{分片序号}.{扩展名}
例如 tick_raw_20240115_09_IF2401_0001.csv.gz。其中交易日用 YYYYMMDD 保证字典序与时间序一致;小时用两位 HH;分片序号用四位补零,用于同一小时内因文件大小上限而切出的多个分片。扩展名保留 .csv.gz 这类“内容格式 + 压缩格式”的双后缀,便于下游按压缩方式选择读取器。命名中不要出现空格、中文和大小写混用,避免跨平台脚本解析差异。
^tick_raw_\d{8}_\d{2}_[A-Z0-9]+_\d{4}\.csv\.gz$,防止人工补文件时破坏规范。元数据描述文件:让目录“自解释”
目录和文件名只能表达时间与品种,无法表达行数、时间范围、字段结构、校验值等检索必需信息。因此每个分片应配一个同名 .meta.json 元数据文件,与数据文件放在同一目录。建议字段包括:数据域、交易日、小时、品种、分片序号、首笔时间、末笔时间、记录行数、字段列表、压缩方式、文件字节数、校验值、生成程序版本。示例:
{
"domain": "tick_raw",
"trade_date": "20240115",
"hour": "09",
"symbol": "IF2401",
"shard_seq": 1,
"first_ts": "2024-01-15T09:00:00.120",
"last_ts": "2024-01-15T09:59:59.870",
"row_count": 182340,
"columns": ["ts","symbol","last","volume","bid1","ask1"],
"compression": "gzip",
"bytes": 5242880,
"checksum": "sha256:...",
"writer_version": "archiver-1.2.0"
}
元数据的作用不只是“记录”,更是快速定位的索引来源。检索时先读元数据判断时间范围与行数,命中后再解压读取数据文件,可以避免大量无效解压。对于跨分片查询,可以先扫描目标交易日下所有 .meta.json,按 first_ts 与 last_ts 过滤出候选分片,再按 shard_seq 顺序读取,保证结果时间有序。
可扩展组织与快速定位的落地要点
第一,分片边界要稳定。小时分片按自然小时切分,跨小时的那一笔归属以“成交时间戳所在小时”为准,并在元数据中记录首末时间,避免边界重复或遗漏。第二,单文件大小设上限。例如单分片压缩后不超过 64MB(示例阈值),超过则在同一小时内递增 shard_seq,既控制单文件读取内存,又保持目录不膨胀。第三,写入采用“临时文件 + 原子重命名”,先写 .tmp 再改名为正式文件,防止归档过程中被检索程序读到半截文件。第四,预留扩展位:数据域目录、年目录、小时目录都是可增删的层级,新增数据域不影响既有路径;若未来需要按“半小时”或“自定义窗口”分片,只需在小时层下增加窗口标识,文件名同步增加窗口字段即可,不必重构整棵树。
最后需要强调,目录规划不是一次性设计,而是要与写入程序、校验脚本、检索接口三者对齐。写入端按规范生成路径与元数据,校验端定期检查“数据文件与元数据是否成对、命名是否合规、首末时间是否连续”,检索端只依赖目录约定与元数据字段,不依赖具体文件内容格式。做到这三点,归档结构就能在数据量持续增长时保持可扩展,并把“找数据”从模糊搜索变成可预期的路径定位。