“项目重新编排与数据接入”是我最近一个月实际操作的一条完整链路,准确说是我独自维护的一套批处理系统改造记录。旧系统长期依赖人工跑脚本,每天凌晨靠 crontab 串起十几个任务,最头疼的就是上游数据一旦变化,后面所有环节全部白跑。这块工作的意义不在于把代码重写一遍,而在于把散落的任务状态统一起来,把数据接入从“能跑”变成“可重放、可排查、可监控”。如果你也在处理定时调度、外部数据同步、多个系统之间的流转协作,这篇记录应该能给你一些直接可用的思路。
从外部看,这只是一次日常项目重构;从实际内容看,它至少包含了调度建模、状态管理、数据接入规范、异常定位几个层面。这篇博客我按真实工作发生顺序来写,既有踩坑实录,也有最后的沉淀结果,希望能让同类项目的维护者少走几步弯路。
1. 为什么“重新编排”比“优化代码”更紧迫
1.1 旧流程到底乱在哪
我不是一开始就打算做“流程编排”这个概念的,真正推动我动手的是连续几周凌晨的告警电话。旧系统的任务依赖关系是写死在各种脚本里的,比如 A 脚本处理完供应商文件后,直接把数据塞到一张业务表,B 脚本靠 sleep 等几分钟再去读这张表。表面上看没人干预也能跑,但一旦 A 脚本因为上游文件格式变化失败,B 脚本照样会启动,它读取到上一批旧数据后照样生成报告,业务同事第二天早上拿到的很可能是一份带有昨日残留数据的错误结果。
这类问题归纳起来有三个明显毛病。
第一,任务之间没有清晰的中间产物和状态记录。处理到哪一步、成功没有、影响多少行,全部依赖日志文件里的 print 输出。日志一滚动,想定位某个环节的历史执行情况几乎靠猜。
第二,失败后的重试是裸奔式的。脚本挂了就重新执行一遍,但重新执行时不会判断之前有没有写进去半截数据,结果就是重复插入、数据翻倍。而这种翻倍因为当时缺少校验,可能要过一两天才被下游对账发现。
第三,数据接入环节是整个链路里最不可控的。上游发来的文件可能晚到,可能字段顺序调整,可能同一批数据传了两遍。这些情况如果不在入口堵住,后边再严谨的业务逻辑都会被脏数据带偏。
所以这次动手第一步不是去优化某个查询,而是先把“流程边界”画清楚。我把整条链路重新编排成四个独立阶段:数据接入、质量校验、业务处理、结果回写。只允许阶段之间通过可控的入库表和批次号衔接,不允许业务代码直接去读上游原始文件。
1.2 把“脚本串联”升级成“有状态的任务链”
在重新编排之前,我首先定了一个规矩:任何一环执行都必须能回答三个问题——现在跑到哪个环节、上一次为什么停、重新启动从哪里续跑。
传统写脚本的方式很难回答这些问题,所以我参考常见的任务调度思路,引入了一张非常简单的状态表。我用它记录每个批次的执行位置,状态字段只有 pending、running、success、failed、skipped 五种。每次跑批开始时写入一条记录,后面每一步更新这条记录。
这张表的设计很简单,但对整个项目的帮助非常大。它让“重新编排”这件事从逻辑层面落到了数据层面,我不用再通过 ps 命令到处看进程,SQL 一查就知道当天任务卡在哪一步。
sql复制CREATE TABLE pipeline_task (
task_id BIGINT AUTO_INCREMENT PRIMARY KEY,
pipeline_name VARCHAR(64) NOT NULL,
stage_name VARCHAR(64) NOT NULL,
biz_date VARCHAR(10) NOT NULL,
run_seq INT NOT NULL DEFAULT 1,
status VARCHAR(16) NOT NULL,
retry_count INT NOT NULL DEFAULT 0,
context_json TEXT,
error_message TEXT,
started_at DATETIME,
finished_at DATETIME,
UNIQUE KEY uk_pipeline_stage (pipeline_name, stage_name, biz_date, run_seq)
);
context_json 字段是我特别留出来的,用来存每个任务执行时需要恢复的现场信息。比如数据接入阶段执行到一半网络中断,重新拉起时要知道已经处理到哪个文件、读到了哪个偏移量。把这些信息放到状态表里,比写在一个临时文件里靠谱得多,临时文件很容易被系统清理任务误删。
实际编写时我并没有引入一套重量级工作流引擎,而是自己维护了一个小的顺序推进逻辑。原因很现实:一是整条链路只有十几步,复杂度可控;二是重引擎会引入额外的部署和维护成本,一台机器就能跑的批处理没有必要做成分布式服务。顺序推进逻辑本身很简单,读取当天任务列表,按照配置的 stage 顺序执行,前一个 success 才执行下一个。
用这种“半自动编排”的方式,我获得了一个非常大的好处:排错和重跑不需要所有环节一起做。以前文件解析失败,修复后常常把已经完成的数据处理任务再跑一遍,既耗时又容易覆盖人工修正过的结果。现在可以直接把状态改成 pending 后从指定 stage 重跑,前面成功的环节不会再去碰。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据接入:上游数据进库前必须对齐的几件事
2.1 先对齐字段语义,再谈技术实现
数据接入在这个项目里占了相当大的工作量,但我发现真正难的不是写下载脚本或解析代码,而是和上游把口径对齐。以前我犯过一个错误,拿到对方提供的接口文档后,想当然认为“order_time”就是订单创建时间,结果数据接入完成后才发现那是订单最后修改时间,而且有时区标记,有时没有,直接导致下游统计全部失真。
这次我在接入前专门做了一份字段核对表,把所有要接的字段按业务含义、类型、取值样例、是否允许为空、更新频率逐一列出来,然后逐条和上游确认。以下是我使用的简版对齐表,你可以直接参考:
| 字段名 | 业务含义 | 上游类型 | 入库
