3. 行式文本格式(CSV/JSON)在逐笔数据中的适用性剖析

在逐笔数据压缩存储的讨论中,CSV 与 JSON 常被当作“基准格式”:它们不依赖专用编解码库,任何语言都能读写,调试时肉眼可读。但“能读”不等于“适合存”。本节以一份真实的逐笔成交样本为对象,量化 CSV 与 JSON 的存储开销、解析效率与查询灵活性,并给出它们作为基准格式的适用边界。

样本与度量口径

取某交易日某只股票连续竞价阶段的逐笔成交样本,字段为:成交编号(seq)、交易所通道号(channel)、证券代码(code)、成交时间(毫秒时间戳,time)、成交价格(price,单位0.01元)、成交数量(qty,单位股)、买卖方向(bs,B/S)、买方委托序号(bid_seq)、卖方委托序号(ask_seq)。样本共 100 万行,价格区间 10.00~10.99 元,单笔数量 100~5000 股,委托序号为 8 位整数。

度量口径需先固定,否则比较没有意义:存储开销按“未压缩原始字节数”和“gzip -6 压缩后字节数”两档统计;解析效率按单线程逐行解析并累加成交金额的总耗时计;查询灵活性按“按时间区间过滤”“按买卖方向聚合”“按委托序号反查”三类典型查询的实现难度与是否需要全表扫描来评价。以下数值为教学示例中的合理估算,用于说明量级关系,不代表特定硬件上的实测结果。

存储开销:文本的“体积税”

CSV 每行形如 1000001,7,600000,34200123,1050,300,B,88123456,88234567,平均约 62 字节;100 万行约 62 MB。JSON 若采用“每行一个对象”的 JSON Lines 形式,键名重复出现,单行膨胀到约 150 字节,总量约 150 MB;若改为“一个数组包全部对象”,键名只出现一次,但仍需逗号、引号与括号,约 95 MB。作为对照,同样字段用二进制定长结构存储约 33 MB,用列式加字典编码可压到 8 MB 量级。

更关键的是压缩后的表现。CSV 经 gzip 压缩约降至 18 MB,压缩比约 3.4:1;JSON Lines 压缩后约 22 MB,压缩比虽高(约 6.8:1),但绝对体积仍大于 CSV。原因在于文本中大量重复的键名和分隔符恰好是压缩算法的“好素材”,压缩能吃掉一部分冗余,却吃不掉数字被展开成 ASCII 字符带来的根本膨胀——一个 4 字节整数写成十进制文本要 8~10 字节,价格 1050 本可 2 字节存储却占了 4 个字符。

同一逐笔成交样本(100 万行)的存储体积对比 单位:MB;浅色为未压缩原始体积,深色为 gzip -6 压缩后体积(示例估算) CSV JSON Lines JSON 数组 二进制定长 列式+字典编码 62 → 18 150 → 22 95 → 14 33 → 5 8 → 2 0 80 160 MB 注:文本格式的压缩比更高,但压缩后的绝对体积仍显著大于二进制与列式方案。

解析效率:字符到数值的转换成本

CSV 解析的典型路径是:按分隔符切分、逐字段做字符串到数值的转换(strtol/strtod)、组装结构体。JSON 解析则要先做词法分析识别引号与括号,再按键名查表映射到字段,最后同样做数值转换。两者都绕不开“ASCII 数字 → 二进制数值”这一步,而这一步正是逐笔数据解析的主要开销:100 万行、每行 6 个数值字段,就是 600 万次字符串转换。

在示例估算中,CSV 单线程解析 100 万行约需 1.2 秒,JSON Lines 约需 3.5 秒,JSON 数组因需整体构建对象树约需 4.2 秒;而二进制定长格式按结构体直接映射,约 0.15 秒。差距来自两处:一是 JSON 的键名匹配与对象分配,二是文本格式无法做零拷贝的批量读取。对回测、实时风控这类需要反复加载全量逐笔数据的场景,解析耗时会被放大到不可忽视的程度。

查询灵活性:文本格式的真正优势

CSV 与 JSON 的强项不在存储和解析,而在“无需预定义模式即可查询”。用 awk -F, '$5>=1050 && $7=="B"' 就能筛出价格不低于 10.50 元的主动买成交;用 jq 'select(.bs=="B") | .qty' 可直接对 JSON 做方向过滤与字段投影。这类临时性、探索性查询不需要建表、不需要写导入脚本,对数据探查和问题定位极有价值。

但要注意其代价:上述查询都是全表扫描,无法利用时间或委托序号做索引下推;按委托序号反查需要遍历全部行;按方向聚合也要读完整份文件。文本格式的“灵活”是表达上的灵活,不是执行上的高效。若把逐笔数据长期以 CSV 存放并频繁做区间查询,扫描成本会随数据量线性增长。

作为基准格式的优缺点与适用边界

综合来看,CSV/JSON 作为基准格式的优点明确:跨语言、可读、可用通用工具直接处理、便于人工核对与样本交换、无需维护模式版本。缺点同样明确:体积膨胀数倍、解析需字符转换、缺乏索引与类型信息、浮点与整数精度依赖解析器实现、字段顺序或键名变更缺乏约束。

因此适用边界可以这样划定:适合作为小样本交换格式、调试与人工核查格式、数据探查与一次性分析格式、以及压缩方案对比时的“基线”;不适合作为逐笔数据的长期主存储、高频回放数据源或需要低延迟随机查询的在线存储。工程上的常见做法是“双轨”:主存储用二进制或列式压缩格式承载全量历史,同时保留按日或按样本裁剪的 CSV/JSON 导出,供排查与交付使用。

判断是否该用文本格式,可以问三个问题:这份数据会被反复全量加载吗?需要按时间或序号做随机查询吗?体积和解析延迟是否进入性能预算?三个答案都是“否”时,CSV/JSON 是省事的选择;任一为“是”,就应转向二进制或列式方案。

一个可复用的对比记录模板

做格式选型时,建议把比较结论固化成可复核的记录,而不是停留在印象。下表字段可直接用于本节样本或自有样本的对比归档:

度量项记录内容本节示例值
样本规模行数、字段数、时间跨度100 万行 / 9 字段 / 连续竞价 4 小时
原始体积未压缩字节数CSV 62 MB;JSON Lines 150 MB
压缩体积gzip -6 后字节数及压缩比CSV 18 MB(3.4:1);JSON 22 MB(6.8:1)
解析耗时单线程全量解析并累加金额CSV 1.2 s;JSON Lines 3.5 s
典型查询时间区间、方向聚合、序号反查均需全表扫描,无索引下推
结论是否作为主存储 / 仅作交换与基线仅作交换、调试与压缩基线

把这张表填完,格式选择就不再是“习惯问题”,而是有数据支撑的工程判断。CSV 与 JSON 的价值在于降低理解与交换的门槛,它们的边界则在于体积、解析与查询效率——认清这条边界,才能让后续的二进制与列式压缩方案有的放矢。