跳转至

第十四章 · 烂摊子

九月三日,星期四,上午九点。

周其正的邮件是凌晨一点二十发的,抄送纪衡、蒋辉、陈稳。标题:营销结算底层数据三表结构不一致,对账异常,请结算组协助。正文四行,最后一行是:这个问题营销技术部查了两个月,没有结论。

纪衡没有回。蒋辉没有回。收件人里只剩他一个还没有回。

记事本上昨天那一行还在:明天上午把方案发给纪衡。方案在本地文件夹里,三页,数据加载部分,拦截器接入部分,数据一致性保障部分,每页页脚都有他的名字。他昨晚十一点四十写完,保存了,没有发出去。

监控页面上,结算成功率百分之九十九点九九,拦截器正常,连接池使用率百分之六十五。这三个数字他记在脑子里,没写进任何文档。

茶水间的咖啡机下面弯着一个人,袖子卷到肩膀,手里拧着一个螺丝。咖啡机的盖子上卡着一块塑料板,她拧了两下,没拧动,换了一个方向,拧开了。是易文。

"你还会修咖啡机。"陈稳说。

"拧螺丝不需要会。"易文把盖子扣回去,拧上螺丝,拍了拍手,"好了。现在它还是没人调。"

她转过身,看到他手里那个空杯子。

"方案发给纪衡了没有。"

"没有。"

"为什么。"

"……还没想好怎么说。"

"你写都写完了,你跟我说还没想好怎么说。"

"发出去就收不回来了。"

易文倒了一杯水,喝了一口。杯壁上有一圈旧的茶垢,洗不掉的那种。

"你跟我来。"

电梯里她按了一楼。

"去哪里。"

"数据治理。你跟我一起。"

"什么数据治理。"

"到了你就知道了。"

停车场的水泥地面反射出一层白。她的车是白色轿车,车内没有挂饰,没有摆件,储物格里一瓶水、一个充电器。中控台的导航亮着,目的地是恒澜科技园区二号楼,距离两公里。

她没开空调,摇下车窗,风是热的。两个路口,右转,进了二号楼的地下车库。熄火之后车里安静了三秒。

"哪个项目。"陈稳说。

"没有项目。就是一个烂摊子。"

"什么烂摊子。"

"营销结算对账系统的底层数据,有三个表,结构不一致,数据对不上。营销技术部的人搞了两个月没搞定,上周五找到我们部门,说让数据组帮忙看看。我看了,确实乱,但也不是不能做。"

"周其正凌晨一点二十发了邮件。抄了纪衡和蒋辉。"

"我知道。他昨天下午先问过我,我让他发邮件。"

这件事陈稳不知道。他昨天下午三点还在改那份三页方案,改到第二页的时候删掉了一整段。

"那为什么叫烂摊子。"

"因为没有人愿意接。所有人都说,这不是数据组的事,这是营销技术部自己没做好,不应该我们擦屁股。我组长说,这个月还有两个分析需求没做完,不做这个。我隔壁组的人说,这个数据太脏了,做出来也说不清楚。我上周五看完,跟组长说,我做。组长说,你自己想清楚,做这个不带任何绩效。我说,我知道。"

那三个表陈稳见过。蒋辉那份十六页方案的第九页附了一张表结构截图,字段名不一致的地方蒋辉用红框圈了三处,正文里一个字都没写怎么处理。这件事他没有告诉易文。

"你怎么不说话。"

"……说不接的人,都说对了。这不是你的事。"

"是。不是我的事。但这个东西在这里,没人做,它就在这里。两个月,三个月,半年,它会变脏,但不解决。直到有一天,有人做系统升级,发现数据对不上,查出来是底层的锅,然后就被记住了。但不是记住做的人,是记住出问题的人。"

八月十一号那次索引被删,执行账号是 root@localhost,Git 里没有对应提交,DHCP 的分配日志按周滚动清掉了。没有人做,它就在那里。这一条陈稳在记事本上写了三遍,日期不同,内容一样。

"你听懂了没有。"

"……听懂了。不想做它的理由全是对的,但留着不解决,它迟早会变成你的问题。"

"差不多。但还有一个原因。"易文刷了工牌,闸机弹开,"我做这个,是因为不出现在任何人的 OKR 里,所以没人管我怎么做。我做完了,数据清楚了,以后谁做对账、谁做报表、谁做分析,底层数据都是干净的。他们不会记住我,但他们每次用到的数据,都是我做过的。"

闸机后面那条走廊的灯只亮了一半,另一半的灯管拆了,管口用胶带封着。

"你组长知道你今天带我过来吗。"陈稳说。

"知道。我跟他说,我借结算组一个人,两个礼拜。他问我借谁,我说陈稳。他说没听过。"

"……然后。"

"然后他说,你自己搞定,别走流程。走流程就要立项,立项就要挂到部门 OKR 上,挂上去就有人要看进度。"

这句话陈稳听懂了两层。上一层是易文说的那层,下一层是:这个项目在系统里不存在,所以他做多少,人事系统里都是零。

三楼那间会议室二十平米,门牌是一张打印纸,用透明胶带贴在门上:数据治理项目组——临时。里面一张长桌,一台显示器,一个键盘,一个鼠标,一本翻开的笔记本,纸上写满数字和简写。窗帘拉了一半,阳光落在桌面上。屏幕亮着,左边是表结构,右边是查询结果。

易文把键盘拉到面前,敲了几下。屏幕上的结果变成一张表,三列:表名、字段数、行数。

"这三个表,分别是营销结算的订单表、支付表、退款表。建表时间分别是去年六月、去年八月、今年一月。建表的人不是同一个人,字段定义没有做过对齐。"

三个表加起来一百多个字段,字段名不一致,类型不一致,空值策略不一致。订单表的主键是 order_id,varchar(64);支付表的主键是 pay_id,int;退款表的主键是 refund_id,varchar(32)。三个表之间没有外键,关联关系靠业务字段拼接,order_id 在支付表里叫 biz_order_no,在退款表里叫 source_order_id。订单表的金额字段是 decimal(18,2),支付表的金额字段是 bigint,单位是分。

还有时间字段。订单表用 create_time,datetime;支付表用 gmt_create,varchar(19),存的是字符串;退款表用 created_at,bigint,存的是秒级时间戳。三个表要按时间关联,得先把三种时间转成一种。转的时候支付表那个 varchar 里有两千三百条是空字符串,不是 NULL,是空字符串。

"这个空字符串,"陈稳说,"是谁写进去的。"

"应用层。去年八月上线的时候没做校验,写了四个月,今年一月才补上。那四个月的数据就是这样。"

"你怎么看。"易文说。

"……统一字段名,统一类型,建视图。"

"多快。"

"如果只是建视图,两天。如果要把数据洗干净,一星期。"

"行。你来做。我不会写 SQL。"

上周三食堂那碗面,她用三个数字把蒋辉的十六页讲完了。她会看数据,这一点陈稳信。她不会写 SQL 这一句,他信一半。

"我认真的。我只会看数据,不会洗数据。你写 SQL,我对结果。你写完,我验证。我们两个,一个礼拜,把这个烂摊子清了。"

"……我手上有蒋辉的项目。"

"你跟蒋辉那个项目,你是配合,不是牵头。你把数据接口层的方案发给纪衡,然后说,数据加载部分已经写好了,拦截器接入部分已经写好了,数据一致性方案有了,接下去只需要配合测试。蒋辉需要你的时候,你出来配合。不需要你的时候,你过来洗数据。"

这件事陈稳知道。他手上七个待办,没有一条前面写着"牵头",全是"配合"和"依赖"。这两个词他在记事本上圈过一次。

"你听懂了没有。"

"……听懂了。配合跟牵头,两件事,可以并行。"

"对。你手上没有主线任务,你只有依赖项。你把依赖项做完了,你的时间就空了。空出来的时间,做这个。"

一百多个字段,三张表,三个人的写法,没有一个人想过以后会有人把它们连起来。老梁说过一句:东西不会说话。东西是死的。只有人会说话。

"想什么。"易文说。

"没什么。"

"你脸上有东西。你在想'这个事不该我来做'。"

他嘴抿了一下。他知道自己抿了。

"你知不知道,你刚才在想什么的时候,你的表情是什么。你嘴抿一下,然后眼睛回到屏幕上。你每次想'这个事不该我来做'的时候,你都是这个表情。"

"……我没有这个表情。"

"你昨天在食堂也是这个表情。我说蒋辉那十六页里有你的拦截器,你抿了一下。"

"你听我说完。这个事确实不该你来做。但如果你不做,它也不会有人做。你等两个月,等到纪衡让你做,你到时候做,没有任何区别。唯一的区别是,你现在做,你就有这个东西。你等一下做,你就是被安排的。"

"……那不一样吗。"

"一样。但一个是你的,一个是别人的。你听懂了没有。"

他没有答这句。他打开数据库,开始看那三个表的数据。四十分钟,四十二次查询,每个表的字段过一遍,把不一致的字段标出来,把空值率高的字段标出来,把重复定义的字段标出来。他在记事本上画了一张表,三列,每列一个表的字段名,用箭头把同义字段连起来。画完,箭头很乱:三条线交叉,四条线平行,还有两条线找不到对应。

易文在对面翻笔记本,没有抬头。

十二点,他放下笔。三号食堂比一楼的难吃,人少。红烧肉,青菜,米饭,饭是热的。

二号楼门口的水泥地面反射出一层白。他拿出手机,打开邮件,找到昨晚写的那份三页方案,从头读到尾,然后按了发送。收件人:纪衡。抄送:蒋辉。标题:营销结算对账系统解耦——数据接口层接入方案。

发送成功的提示弹出来,两秒后消失。

下午两点,新邮件提醒。纪衡的回复一行字:方案收到。拦截器接入部分按你写的来。数据加载部分跟蒋辉确认接口。

他没有回这封。

下午三点,他给蒋辉发消息:数据接口层接入方案已经发给纪衡了,数据加载方法已拆分,接口定义在 MR 里,你确认一下接口格式,我配合改。

蒋辉回:收到。我看一下。

下午四点,蒋辉又回:接口格式没问题,我这边先按这个走,有改动再同步。

他回了一个字:好。

三张表的结构全部过完了。十六个同义字段:五个字段名完全一致,七个字段名不一致但含义相同,四个字段名一致但类型或长度不同。记事本上写了一页,不够,翻页继续写。

他在最后写了一行:建视图,统一字段名,字段类型以订单表为准,空值策略统一为 nullable,金额字段全部转成 decimal(18,2)。

CREATE VIEW,一个字段一个字段地写。订单表的 order_id,支付表的 biz_order_no,退款表的 source_order_id,在视图里统一叫 order_id,varchar(64)。第一个视图十一个字段,二十二行 SQL,检查一遍,语法正确,提交执行。窗外的太阳斜了,车少了一半。

第二个视图十四个字段,退款表那两个找不到对应的字段他先留着,加了注释:待业务确认。第三个视图跑了两次,第一次行数少了三行,是支付表里三条 pay_id 为 0 的脏数据,他加了一个 where 条件把它们排除,行数对上了。

时间字段是最麻烦的一块。他用 STR_TO_DATE 转支付表那个 varchar,遇到空字符串直接报错,加了一层 NULLIF 包起来,空的转成 NULL。退款表那个秒级时间戳用 FROM_UNIXTIME,转出来有十七条落在 1970 年,是写入的时候传了 0。这十七条他没有排除,加了一个字段标记:time_invalid,布尔值。

"为什么不删。"易文在对面问。

"因为它们的金额加起来是四千三百二十块。删了对不上账。"

"记下来。这句话你以后要说给纪衡听。"

下午六点,三个视图,六十八行 SQL,一百多个字段压到四十二个,字段名、类型、空值策略统一。三个视图都创建成功,查询结果正确,数据行数对得上。

记事本上,"建视图"后面打了一个勾,下面写:明天开始洗数据。再下面一行:先把字段空值率超过百分之三十的列标记出来,逐列确认是否可丢弃。

"写完了没有。"易文背上包。

"三个视图写完了。明天开始洗数据。"

"行。明天早上九点,会议室见。"

门关上以后,他把那三个视图又查了一遍,字段名对着记事本核了一遍,没有问题。他在记事本上补了今天这一条:上午跟易文去二号楼,数据治理项目,三个表,一百多个字段,建了三个视图统一字段定义。

蒋辉那个 MR 他也打开看了。接口格式确实没问题。他没有改任何东西,也没有留评论。

地铁上没有座位。他抓着扶手,窗外是黑的隧道。

到家十点。易文的消息发在二十分钟前:视图发我看看。

他把三个视图的 SQL 文件发过去,正文一行:三个视图,字段统一了,你先看看数据对不对。

易文没有回复。

牛奶一杯,凉的,胃没有反应。他喝完,把杯子洗了,放在架子上。

易文的回复在五分钟以内:没问题。明天洗数据。

他回了一个字:好。

对面那栋楼的窗户一格一格亮着。他看了十一个月的那扇窗,还是黑的。

记事本上明天的待办,两条。第一条:上午九点,二号楼,洗数据。第二条:下午,确认蒋辉的接口格式。他在第一条后面又加了一行小字:问易文,那两个待确认的字段找谁确认。

卧室的天花板上没有裂缝。这间卧室没有裂缝。


← 第十三章 易文 · 目录 · 第十五章 翻译 →