下一章 上一章 目录 设置
3、第3章:风暴 推开那扇沉 ...
-
推开那扇沉重的玻璃门,L像一头被彻底激怒的困兽,猛地从办公桌后站了起来。他的脸色铁青,镜片后的眼睛因为极致的愤怒而布满了狰狞的红血丝,死死地瞪着我,胸膛剧烈起伏。办公室里那股冷冽的木质香水味,此刻似乎也被一种更原始的、暴戾的气息所取代。
“你!删我?!” 他几乎是咆哮出来,声音因为过度激动而扭曲变调,手指颤抖地指向我,那枚精致的银色袖扣在昏暗光线下闪烁着危险的光,“谁给你的胆子?!啊?!”
玻璃门在我身后无声合拢,隔绝了外面格子间所有若有若无的视线。L的办公室像一个骤然加压的密封舱。窗外,蛇口方向的海面上,铅灰色的云层正翻涌着压向海岸城密集的楼群,一道惨白的闪电无声地撕裂天际,几秒后,滚雷沉闷地碾过深圳湾,震得脚下的地板隐隐发颤。
“我问你话!”L的声音拔得更高,近乎尖利,他绕过那张巨大的黑色办公桌,锃亮的Ferragamo皮鞋踩在冰冷的浅灰色大理石地面上,发出咄咄逼人的脆响,几步就逼近到我面前不足半米的地方。那股混合着冷冽木香和暴怒汗意的气息,像一张湿热的网,兜头罩下。“谁给你的胆子删我的□□?删我的微信?嗯?!”
我强迫自己抬起眼,迎上那双被怒火烧得通红的眼睛。距离太近了,甚至能看清他鼻翼因为急促呼吸而微微扇动的纹路。心脏在肋骨下擂鼓,但一种奇异的冰冷感却在四肢蔓延开。删掉他的那一刻,我就知道没有回头路了。
“L经理,”我的声音出乎意料地平稳,带着一种刻意拉开的距离感,“工作沟通,公司有邮件系统。私人社交软件,我有权管理自己的联系人列表。”
“管理?”他像是听到了天大的笑话,猛地俯身,双手“砰”地一声重重拍在我身体两侧的门板上,将我彻底困在他和冰冷的玻璃之间。那巨大的声响在密闭空间里回荡。“苏蔓!你搞清楚自己的位置!我是你的直属上司!在深圳,在科技园,在这个项目组里!你的‘管理’就是擅自切断和上级的必要沟通渠道?!”
他的唾沫星子几乎喷到我脸上。那股被冒犯、被挑战的狂怒,像实质的火焰从他每一个毛孔里喷发出来。
“必要沟通?”我直视着他,声音不大,却清晰地穿透他粗重的喘息,“上午十一点三十二分,您通过□□发送:‘中午想吃什么?听说海岸城新开了家顺德菜。’ 下午两点十五分:‘看你今天脸色不太好,晚上我送你?顺路。’ 就在刚才,三点二十五分:‘数据清洗可以简化,晚上南头古城那家精酿吧,我们慢慢过。’”我顿了顿,清晰地吐出最后一句,“L经理,这属于项目组‘必要沟通’的范畴吗?如果是,我建议您通过公司邮件系统发送,抄送项目组全体成员和HR备案。”
空气瞬间凝固了。
L脸上的暴怒像被按了暂停键,狰狞的表情僵在那里,随即转化为一种难以置信的惊愕,最后沉淀为被戳穿伪装的、更加深沉的阴鸷。他死死地盯着我,眼神无情且冰冷刺骨,试图从我脸上找出哪怕一丝动摇或恐惧。
窗外又一道闪电划过,惨白的光映亮了他眼底那抹冰冷的算计和……一丝被猎物反咬一口的狼狈。
他缓缓地、极其缓慢地直起身,收回了拍在门板上的手,整理了一下因为激动而微微歪斜的银灰色领带结。那个动作带着一种刻意找回掌控感的僵硬。
“好,很好。”他退后一步,声音低沉下去,却比刚才的咆哮更令人心悸,每一个字都像是从冰窖里捞出来的,“跟我谈规则?谈界限?苏蔓,你很有种。”他扯出一个极其冰冷的笑,“看来是我看走眼了,你不是潜力股,你是颗不知天高地厚、随时会炸毁整个项目的——定时炸弹。”
他转身,大步走回办公桌后,猛地拉开真皮座椅坐下,巨大的动作带着椅子发出刺耳的摩擦声。他不再看我,目光投向电脑屏幕,手指在键盘上飞快地敲击,发出密集而冷酷的哒哒声。
“数据清洗任务,五点前,最终结果发我邮箱,抄送风控王总监。”他的声音恢复了那种居高临下的命令口吻,不带任何情绪,仿佛刚才那场激烈的对峙从未发生,“这是死命令。做不完,后果自负。出去。”
没有咆哮,没有威胁,但这冰冷的指令比之前任何一次都更具杀伤力。他知道我删除了他的即时通讯方式,他掐断了我任何可能寻求帮助或解释的快速通道。五点,成了悬在我头顶的达摩克利斯之剑。
我沉默地转身,推开沉重的玻璃门。外面格子间的空气似乎都带着小心翼翼的窥探。陈浩担忧的目光立刻投过来,我微微摇头,径直走回自己的工位。
没有时间愤怒,没有时间委屈。距离五点,只有一百分钟。
我深吸一口气,强迫自己将所有杂念排出脑海。手指放在键盘上,冰凉的触感让我稍微冷静。屏幕上,那个庞大的名为“user_behavior_raw_20230915.csv”的数据文件,静静地躺在那里,接近30GB的大小,像一座沉默的钢铁堡垒。常规的Pandas读取和清洗,在这种量级下,光是加载就可能耗去几十分钟。
五点前完成清洗加验证?天方夜谭。
除非……剑走偏锋。
一个念头在绝望中闪现。我迅速打开公司内部的代码库平台,指尖在键盘上飞舞,输入关键词:分布式清洗框架。几秒后,一个内部项目跳了出来——由基础架构组半年前开发的“SparkStreaming_DataWash_Template”,一个基于PySpark的分布式数据清洗模板框架,专为处理海量日志设计!
心脏猛地一跳。希望的火苗微弱地燃起。这个框架我半年前参与过测试,但因为当时项目组数据量还没这么大,一直没用上。它的核心思想是将清洗规则拆解成可配置的步骤,利用Spark集群的分布式计算能力,把数据切割成无数小块,并行处理!
我立刻点开框架文档,飞速浏览。需要配置清洗规则脚本(rules.py),定义输入输出源,然后提交到公司的Spark on Yarn集群……
时间!最缺的就是时间!熟悉框架、编写规则脚本、测试、部署、监控任务……每一项都需要时间!
我立刻点开内部通讯软件,找到基础架构组的接口人张工的头像——一个戴着厚厚眼镜、头发有些稀疏的技术男。来不及打字,直接拨通语音通话。
“喂?张工?紧急求助!”我的声音带着自己都未察觉的急促,“我是业务研发三组的苏蔓,现在有个海量数据清洗任务,必须五点前出结果!想用你们那个Spark清洗框架,但我没实际部署过,规则脚本也还没写……”
“Spark清洗框架?现在?”张工的声音从耳机里传来,带着一丝被打断工作的错愕,“苏蔓?数据量多大?规则复杂吗?现在集群资源很紧张,提交任务要排队,而且你这规则脚本……”
“30G!规则很复杂!”我飞快地打断他,“文档我看了,规则脚本我现在就开始写!张工,救命!这个任务直接关系到风控模型的紧急上线!L经理亲自盯着五点要结果!拜托了!流程上需要你这边审批资源,帮我加个急塞进队列行吗?脚本我保证在半小时内写完发你审核!”
电话那头沉默了几秒。在科技园,搬出“紧急上线”和“L经理亲自盯”这两块招牌,往往能撬动一些资源。
“……行吧。”张工叹了口气,透着一股“又是这种破事”的无奈,“你现在就写脚本,写完立刻发我,我帮你看看。同时,你登录集群管理平台,先用你的账号申请资源,选‘紧急任务’通道,申请理由写清楚,我这边看到申请就立刻批。记住,脚本别写崩了,集群搞挂了我们都担不起!”
“明白!万分感谢张工!”我几乎是吼出来的,立刻挂断电话。
屏幕上,PyCharm的界面已经打开。新建文件,命名“user_behavior_clean_rules.py”。指尖在键盘上化作残影。大脑以前所未有的速度运转,将上午会议上L定下的那十几条复杂清洗逻辑,一条条转化为PySpark的函数和UDF(用户定义函数)。过滤异常IP段(深圳本地IDC和海外某些高风险的)、剔除机器爬虫特征(请求频率、Header异常)、关联用户画像表剔除黑名单用户、修复时间戳时区错误(统一为北京时间GMT+8)、处理缺失值(用同类型用户均值填充)……
汗水顺着额角滑下,滴落在键盘上。我浑然不觉,整个世界只剩下屏幕上跳跃的代码和脑海中飞速构建的逻辑链条。时间一分一秒流逝,屏幕上代码行数飞快增加。
“叮咚。” 内部通讯软件提示音。
是张工:“脚本写完没?集群资源申请我看到了,给你批了最高优先级,但前面还有两个任务在跑,估计还要等十五分钟左右才能轮到你的任务启动。你脚本快点!”
十五分钟!我扫了一眼屏幕右下角:16:05。距离五点只剩五十五分钟!而脚本,还有最后两条规则没写完!
“马上!最后两条!”我飞快回复,指尖敲击得更快。
16:12。终于,敲下最后一个句号。来不及仔细检查,立刻将“user_behavior_clean_rules.py”文件拖进聊天窗口,发送给张工。
“张工,脚本好了!麻烦火速审核!”
等待回复的每一秒都像一个世纪。我死死盯着聊天窗口,又忍不住瞥向L办公室的方向。他依旧坐在那里,侧对着外面,看不清表情,但那股冰冷的压迫感仿佛穿透了玻璃墙。
16:18。张工的头像终于跳动。
“看完了,逻辑大体没问题。有几个小地方优化了一下,帮你改了发回给你。另外,输入输出路径你配置了吗?用HDFS路径!还有,清洗后的数据你最后需要落地成什么格式?CSV还是Parquet?建议Parquet,压缩比高,读写快!”
“Parquet!输出路径我马上配!”我立刻下载他发回的修改版脚本,同时登录公司的大数据管理平台,找到HDFS目录。输入源是原始数据路径:/data/raw/user_behavior/20230915/。输出路径……我飞快地新建一个目录:/data/clean/user_behavior/20230915_emergency/。
配置好路径,将最终版的“rules.py”上传到HDFS指定位置。然后,登录Spark集群任务提交页面。选择任务模板“DataWash_Template”,填入清洗规则脚本在HDFS的路径,输入输出路径……最后,在任务名称里,我敲下:“Emergency_Clean_Task_For_RiskModel_By_SUMAN”。
深吸一口气,点击:提交!
屏幕上跳出提示:“任务 [Emergency_Clean_Task_For_RiskModel_By_SUMAN] 已提交,状态:PENDING(等待资源)。”
心脏悬到了嗓子眼。现在,只能等待。祈祷前面两个任务快点结束,祈祷集群资源顺利分配,祈祷我的脚本在分布式环境下运行无误!
时间:16:28。
格子间里,下班前的躁动已经开始蔓延。有人收拾东西,有人小声讨论晚饭去哪解决——海岸城的“八合里”牛肉火锅?还是深大桂庙新村新开的“张姐烤鱼”?小雅哼着歌,把工位上的多肉植物喷了喷水。这一切的轻松,都与我无关。
我死死盯着集群任务监控页面。那个任务状态,顽固地显示着:PENDING。
16:35。状态终于变了:ACCEPTED(已接受)!正在分配资源!
16:38:RUNNING(运行中)!
监控页面上,开始刷出日志。看到Executor(执行器)被一个个启动,看到数据被分成几百个partition(分区)开始并行处理,看到清洗规则被一步步应用……进度条,极其缓慢地,开始向前蠕动。
1%... 2%... 3%...
这速度……我的心一点点沉下去。照这个进度,别说五点,六点都够呛!
怎么办?验证脚本还没跑!就算清洗完了,没有验证,L和风控组怎么可能认账?
绝望再次攫住了我。难道真的……无路可走了吗?
就在这时,陈浩的椅子滑了过来。他压低声音:“蔓姐,怎么样了?我看你一直在搞Spark……能行吗?”
我苦笑着摇摇头,指着那缓慢爬行的进度条。
陈浩凑近屏幕看了看,眉头紧锁。他忽然说:“验证脚本……是不是就是那套检查数据完整性、一致性的规则?我记得风控那边给过一个Python脚本包?”
“对!”我眼睛一亮,“但那个脚本也是跑单机的,数据量这么大,跑一遍也得几个小时!”
陈浩推了推眼镜,镜片后闪过一丝光:“单机跑肯定不行。但是……蔓姐,你清洗任务用的是分布式,清洗后的数据已经按用户ID或者其他键值分布存储在HDFS上了。验证脚本的核心逻辑,其实也是基于规则对清洗后的数据做各种统计和断言(Assertion)。我们能不能……把验证脚本的规则,也拆解成分布式任务?比如,让每个partition自己先做一部分统计汇总,最后再合并结果?”
分布式验证!
仿佛一道惊雷劈开了迷雾!对啊!验证为什么不能并行化?验证的规则,比如“清洗后数据量不应少于原始数据的75%”、“特定错误码出现频率应低于0.1%”、“用户地域分布应符合历史基线”……这些都可以拆解成一个个小的统计指标,分发到各个partition去计算局部结果,最后再汇总全局结果进行判断!
“浩子!你真是天才!”我激动得差点喊出来,立刻点开风控给的那个验证脚本包。里面是几十个独立的Python脚本,每个负责一项验证指标。
时间:16:48。进度条:15%。
没时间全部重构了!必须抓大放小!我迅速浏览脚本,挑出最核心、L和风控最关注的五项指标:数据总量保留率、深圳本地用户占比变化、高危操作频率、关键字段缺失率、时间戳连续性。
然后,我再次打开PyCharm,新建一个脚本:validation_distributed.py。以飞快的速度,将这五个核心指标的验证逻辑,改写成PySpark的统计任务!利用Spark的groupBy、agg、filter等操作进行初步汇总,最后再用collect()或take()拉取少量关键结果到Driver(驱动节点)进行最终判断!
指尖在键盘上跳跃,汗水浸湿了后背的衣服。窗外的天色已经完全黑透,暴雨倾盆而下,密集的雨点猛烈敲击着公司大厦高层的玻璃幕墙,发出持续的、令人心悸的轰鸣。楼下的深南大道,车流在雨幕和霓虹中拖曳出长长的红色光带。地铁1号线深大站A3出口,下班的人群顶着风雨,行色匆匆。
办公室里,灯光显得格外惨白。
17:03。清洗任务进度条:65%。验证脚本validation_distributed.py终于写完!
我毫不犹豫地再次登录任务提交页面。新建任务!任务名:“Emergency_Validation_Task_For_RiskModel_By_SUMAN”。选择模板?没有现成的!直接上传我刚刚写好的脚本!配置输入路径指向清洗任务即将输出的那个HDFS路径!输出?直接打印日志和关键结果到控制台!
提交!
第二个任务状态也跳成了:PENDING。
时间:17:05。距离L规定的死线,已经过了五分钟。
我猛地抬头看向L的办公室。他不知何时已经站到了玻璃墙前,双手抱胸,面无表情地看着我这边,眼神像在看一个已经倒计时归零的炸弹。那目光,冰冷,嘲讽,带着一种等待审判的笃定。我好像听到了他在对我说三个字:
“时、间、到。”