TT Lab
开始
学习 学习路径 课程

处理客户数据

文件名没变,里面变了

在 TT Lab 中继续学习

一句话总结

别人提供的文件,其 schema 会在不通知的情况下变化。所以接收方要把指纹固化下来,并把哪些会被破坏、哪些不会被破坏写死在代码里,由程序自动判定。

为什么需要它

每周一都会收到同名的文件。连续 12 周都没有问题。到了第 13 周,仪表板上的销售额变成了 0。打开文件一看,有一个列名从 amount_krw 变成了 amount。发送方说“整理了一下字段名”。他们没有通知的原因很简单:在他们看来,这并不是会让我们的管道停下来的事。

这种事反复发生,有结构性的原因:发送方只是修改了自己系统的 schema,而接收方一直把它当作契约在使用。这两个事实之间如果没有文档,变更就总是悄悄到来。而且我们无法控制修改的一方。如果是生产数据库,可以让新旧 schema 共存并逐步迁移,而别人的文件没有这样的余地。

所以能做的只有一件事:让管道先发现“已经变了”这个事实。

工作原理

从收到的文件中提取 schema,固化成指纹。指纹中要包含列名和顺序、为每一列推断出的类型,以及值的样本。只把名称和类型连起来做哈希,就得到一个简短的指纹,这个指纹如果与上周不同,就说明有东西变了。

类型推断要先确定尺子:去掉空值,剩下的值全都是整数的样子,就视为 int;包含小数的,视为 float;全部是 YYYY-MM-DD 的,视为 date;其余视为 str。这是推断而不是声明,所以必须把用了什么尺子写在代码里,之后出错时才知道该改哪里。

然后与上周的指纹核对,把变化分成五种。

类型 看到什么 如何识别
新增 出现了原来没有的列 列名集合的差
删除 原来有的列没有了 列名集合的差
重命名 消失了一个,又多出一个 消失的列和新出现的列,值的样本是否大量重合
类型变更 同名列的类型变了 比较推断出的类型
语义变更 什么都看不到 靠 schema 抓不到,只有从值的分布才能看出来

用值来识别重命名是关键。只看名称,是消失了一条、新增了一条,共两件;但如果值的样本几乎相同,就是同一列只换了名字。有了这个判定,管道才能回答“加一条映射就行了”。

最后一行是本实验最重要的一点。语义变更靠 schema 检查永远抓不到。金额单位从韩元变成千韩元,列名和类型都没变。检查全部通过,而销售额只剩下千分之一。

在现场相遇的样子

第一,被不认识的列吓得停下来。发送方为了自己的需要多加一列,是常有的事,我们的加载没有理由因此停下来。所以规则的默认值是不认识的列放行。反过来,消失的必需列则中止。这两条是兼容规则的骨架,其余的都落在这两者之间的某处。

第二,不区分必需和可选。没有区分,所有的删除就会被同等对待,结果没有人再去读警告。必需列的清单是由业务决定的,而不是由数据决定的。

第三,把类型变宽和类型被破坏同等对待。int 变成 float,通常只是改成了小数写法,计算照样可以进行。int 变成 str,合计会变成字符串拼接,或者抛出异常。把两者放在同一个等级,警告就没有用了。

第四,不看分布。识别语义变更的唯一办法,是把值的分布与上周比较。如果中位数跳升到三倍以上,或者跌到三分之一以下,就需要人来看。这不是证据,而是线索:也许那一周真的有大额交易集中出现。把线索交给人,是机器分内的事。

实际工作中真正重要的事

下一项实验要做什么

运行一个把同样的 30 条订单在七周内只改变 schema 并导出的复现器,建立接收目录,然后把 schema 工具 schema.py 一步步做大:固化指纹,分类新增和删除,用值的样本找出重命名,区分类型变更,用 contract.json 声明兼容规则,自动判定 pass、warn 和 stop。最后,通过值的分布抓出名称和类型都不变而只有单位变了的列,用数字展示 schema 检查看不到什么。