凌晨3点,PagerDuty(告警工具)响了。一个数据处理器挂了,而罪魁祸首是第23487行的一个逗号。
这事得从两周前说起。我给供应商搭了个CSV解析器,测试文件跑得挺顺,推了生产环境。供应商的导出格式突然变了——不是全改,只动了某些行的某个字段。他们在产品名里加了个没引号包裹的逗号。
预期格式是这样的:product_name,price,stock → Widgets,12.99,45
但第23487行变成了:Widgets, Premium Edition,12.99,45
就这一个逗号,整行错位。解析器把"Premium Edition"当成价格,转float时直接崩溃。
排查一小时,怀疑人生三次
我第一反应是正则写劈了。对着regex盯了半小时,没问题。
第二反应是编码问题。UTF-8?BOM头?都查了,干净得很。
最后打印原始行,才看见那个裸奔的逗号——藏在产品名中间,没双引号包裹,CSV标准里的经典陷阱。
测试为什么没抓到?因为前2万行都是干净的。格式bug只在产品名包含特定关键词时出现,而我的测试集里一个都没有。
更蠢的是:我没写任何校验。Python的csv模块默默解析完,没报错,我就以为数据没问题。这是典型的「沉默即正确」幻觉。
补上的校验代码长这样
核心就两件事:数列数对不对,价格能不能转数字。不对就记日志、跳过、别炸。
代码不复杂。先检查len(row)是否等于预期列数,再try-float第二列。任一失败就标记为脏数据,继续跑下一行。
关键是别让脏数据混进下游。我之前假设供应商的数据永远规范,这个假设在凌晨3点让我付出了代价。
该用生产数据做测试,而不是拿干净的样本自欺欺人。Dry run模式(试运行)能在不吵醒任何人的情况下发现这类问题。
这个bug的教训不止于CSV
它暴露的是数据管道的通病:上游的微小变化,下游的灾难性后果。供应商改格式时不会通知你,他们的「随机」改动在你的系统里是确定性崩溃。
我见过更狠的案例。某金融公司的ETL(数据抽取转换加载)流程,因为上游日期格式从YYYY-MM-DD变成MM/DD/YYYY,导致整月报表错误。发现时已经发了给董事会。
CSV没有强制schema(数据模式),这是它的设计特性,也是它的原罪。JSON有类型,Parquet有schema校验,而CSV靠「约定俗成」——约定这东西,在跨组织协作时基本等于没有。
防御策略其实就几条:校验第一行和最后一行之间的每一行,对上游做契约测试(contract testing),永远保留一个「拒绝并报警」的默认分支。
那个凌晨3点的告警,后来成了我们团队的入职故事。新同事的第一周作业:给这个解析器写 fuzzing(模糊测试),随机往CSV里塞垃圾数据,看能不能让它优雅降级。
供应商至今没承认那次格式变更。他们的changelog(变更日志)里,那两周是空白的。
你的数据管道里,有没有某个「肯定没问题」的假设,正在等一个凌晨3点的电话?
热门跟贴