1. 从被数据管道淹没,到认真写一个DataOps博客
做了快十年数据平台相关的工作,一个非常现实的问题是:身边真正把DataOps讲清楚的人,远比真正把DataOps落地的人少。这个词被引进来之后,很多团队第一反应是“这不就是DevOps套到数据上吗”,然后买了一堆调度工具,把Airflow装起来,开几个自动化任务的会,就觉得DataOps已经做完了。我见过太多团队在半年后回到原点,管道照样挂,口径照样乱,数据延迟照样没人说得清楚什么时候能修好。
我自己的情况也差不多。早几年负责一个数据平台,每天早上的第一个动作不是看业务报表,而是打开任务监控列表,看昨晚的批处理有没有挂,挂在了哪一步,谁的数据没跑出来。那种每天从“灭火”开始的体验,持续了很长一段时间。后来我意识到,问题不在于某一条任务写得不好,而在于整个数据交付的过程缺少工程化的约束。数据管道本质上是一套软件系统,但它长期被当作“写完SQL能出数就行”的野生产物来维护,这本身就是最大的隐患。
这就是我决定认真写Liking‘s DataOps Blog的原因。这个博客不是列名词解释,也不是给某家厂商的产品写软文,而是把我自己从“每天捞数据、修任务、对口径”的泥潭里爬出来的过程,以及过程中沉淀下来的方法、工具和教训,一件件记录下来。这篇内容算是博客的第一篇正式文章,我会把DataOps在真实工程环境里到底解决什么问题、哪些概念被过度包装、落地时真正要动的地方是什么,一五一十说清楚。
如果你是被“数据管道频繁失败”“业务口径对不上”“数据交付没有节奏感”这些问题困扰的数据工程师、数据平台负责人,或者你的团队已经在用一些数据工具但总觉得差一口气,那这篇文章应该对你有用。没有PPT式的框架图,只讲落地过程中真正起作用的细节。
2. 关于DataOps,先撕掉几层认知偏差
2.1 DataOps不是DevOps的数据专用版
很多文章喜欢用一个简单的类比:DevOps管代码,DataOps管数据。这个类比方向没错,但如果照搬DevOps的实践到数据领域,很快就会发现不对劲。
代码的构建和发布有一个非常清晰的分界:代码在测试环境验证通过,合并到主干,打包发布到生产,整个过程可以做到高度标准化,回滚也相对干净——上一个版本就是上一个版本,直接切换即可。数据的构建则完全不同。一个数据任务跑完,产出的不是可以发布的“交付物”,而是一份状态。这份状态一旦被下游消费,想回滚就不是把任务切回上一个版本就能解决的,你还要考虑下游已经基于错误数据做了哪些决策、哪些报表已经被导出、哪些模型已经被训练。数据回滚从来不是“切版本”,而是“修正真相”。
另一个关键差异是测试的语义。DevOps里的单测,是针对某个函数、某个模块的确定性断言:输入固定,输出可预期。数据任务里经常面对的是“数据分布漂移”:上游业务系统改了订单状态码,下游清洗逻辑没有跟上,结果某一天产出数据的值域范围变了,但任务本身没有报错。单测能发现的往往只是表结构对没对上、非空约束有没有被满足,很难发现“单量的环比暴涨了20倍,但其实是因为上游把取消订单也算进去了”。这种情况,测试通过,数据却是坏的。
我见过不少团队用DevOps的思路上来就给数据任务加了严苛的CI流程,结果大量时间耗在让“任务能跑过检查”上,真正该关注的数据质量反而被忽略了。DataOps一定需要借鉴DevOps的工程化手段,但不能把手段当目的,数据和代码在本质上的差异决定了做法必须有自己的逻辑。
2.2 工具堆出来了,不等于DataOps就落地了
2020年前后,数据工具开始爆发,编排、血缘、质量、元数据、数据资产,每个赛道都有一堆产品。这时候出现了一种很典型的“工具幻觉”:把市面上所有热门的东西都集成一遍,数据质量平台、数据目录、数据血缘、任务编排,加起来十几个系统,看起来什么都有,但数据平台的现状没有任何改善。
为什么?因为这些工具彼此独立,数据孤岛变成了“工具孤岛”。血缘系统画出来的链路图和实际的调度依赖对不上,质量平台每天出了一堆告警但没人处理,编排平台的DAG越画越复杂,核心的靠人工维护的定时任务还是照旧。每一个工具都在“运行”,但它们没有连成一条真正的流水线。
DataOps的核心价值在于“端到端地看数据交付这件事”。从业务系统的数据产生,到加工清洗,到进入数仓/数据湖,再到提供给分析、报表、模型消费,这中间的所有环节要当成一条流水线来设计。如果买了工具但不去动流程本身,工具再多也只是给原本混乱的过程增加了一层新的混乱。
2.3 DataOps与数据治理、Data Fabric不是一回事
这个认知偏差在行业里很常见:有人把DataOps理解为数据治理的工程化升级,也有人把DataFabric和DataOps当成同义词互换着用。从我自己的实践感受来说,这三者的边界其实很清晰,它们的关注点完全不在一个层面。
数据治理的核心是“定规则”:谁来负责这个数据域、这个字段的业务定义是什么、哪些数据涉及隐私需要脱敏、数据的保留周期是多长。治理的产出物是规范、流程、职责,本质上偏“管理”。
DataFabric的核心是“织一层智能的数据访问层”,让用户能通过统一的接口拿到分布式环境中合适的数据,它强调的是架构形态,试图通过虚拟化、知识图谱、主动元数据等技术,让“找数”和“用数”更顺滑,偏“架构”。
DataOps的核心是“把这些事情放到工程化流水线里持续运作”。一个数据平台可以没有严格意义上的DataFabric架构,但DataOps的原则依然适用;数据治理的规范最终一定要落到具体的任务和流水线里去执行,否则治理落地就是一句空话。
我自己的理解是:治理负责定义“正确”是什么,DataFabric负责让数据更好被找到,DataOps负责让数据以可靠、敏捷的方式持续交付出来。三者需要协作,但不能混为一谈。
3. 我在项目中推DataOps的实际动作:从编排到契约
3.1 第一件事:盘点存量数据管道,建立“失败清单”
真正动手做DataOps改进,第一步不是选工具,而是先把现状摸清楚。我习惯的做法是,把当前生产环境所有定时数据任务拉出来,按以下维度清单化:
- 任务的所有人(owner)
- 调度频率与依赖上游
- 近30天失败次数与最频繁的失败原因
- 是否有监控、是否有重试机制、告警是否真的有效
- 下游消费方(报表、接口、算法、下游任务)
- 产出数据的关键质量维度(行数、主键唯一性、异常率等)
这张清单的价值在于把“数据管道很乱”这种模糊的感受,变成了可以量化的“失败任务Top 10”“无人认领任务Top 20”“下游链路不清晰任务Top 10”。没有这份清单前,很多人开会讨论的是感受;有了这份清单,讨论的是具体问题。
我见过最多的“隐性炸弹”是无人认领的任务:任务挂在某台服务器上,每天跑,但没人说得清它是谁建的、产出给谁用。直到某一天,下游的人来找你,说“这个数据停了三天了,为什么没人修”,你才发现这台机器上挂着一个没有任何负责人、没有任何监控的任务。这种任务,DataOps改造的第一步就该下线或者明确归属。
3.2 把数据流水线的CI/CD落到实处
很多团队说自己在做“DataOps”,但每天的流程还是:有人把SQL改了一版,直接在生产环境跑,跑出问题了再回滚。这个流程缺少最基本的工程约束。我在自己的项目里,把数据流水线的CI/CD拆成了四层,每一层都做了对应的检查。
第一层是“代码入库”:所有用于数据加工的任务定义、SQL脚本、配置文件,必须统一放到Git仓库管理,不能只存留在调度平台里。这一步看似基础,但能解决大量实际问题,比如“上个月谁改了什么任务说不清楚”“这个版本的逻辑为什么要改”“事故出在哪个版本”。很多数据团队管不好任务版本,根本原因就是没把任务当代码管理。
第二层是“构建与测试”:
- 语法检查与字段级校验:在任务提交后,用脚本检查SQL语法是否合法,目标表字段是否存在,字段类型映射是否匹配。
- 单元测试:对每个核心清洗逻辑构建“最小用例”,用样本数据验证逻辑正确性。比如“订单状态码999的脏数据应该被过滤”“金额字段为负的记录应该被打标而不是丢弃”。
- 数据质量测试嵌入:用数据质量工具(我在第四部分会细说)在数据产出后自动运行校验,比如“订单明细表主键唯一性”“核心业务表行数与昨日变化不超过10%”“字段空值率不超过5%”。校验不通过的任务,不允许进入下一环节。
第三层是“部署与发布”:数据任务的发布,不能简单等同于“把代码提交到生产”。我习惯把生产数据任务分成两套环境的概念——虽然大多数情况下数据环境和计算资源是同一套,但“预发布阶段”和“正式发布阶段”要分开。预发布阶段先跑一个简化版本,只处理小分区(比如只处理最近1小时或最近100MB的数据),验证新逻辑在实际生产数据上不会出问题;确认无误后,再切换到全量分区跑正式发布。这有点类似灰度发布的概念,在实际操作中可以极大降低大改动的风险。
第四层是“监控与告警闭环”:任务失败要有告警,告警要有认领,认领要有处理记录。很多团队做到“有告警”就停住了,结果告警过多变成“狼来了”:每天几百条告警,习惯了之后连真正的故障都会被忽略。我后来的做法是:告警分级、认领制度和每周失败原因复盘。P0级告警意味着核心业务数据链路中断,要在15分钟内响应;P1级是数据质量异常但链路未断,4小时内处理;P2级是可延后处理的告警。每周固定时间把一周的失败任务清单过一遍,找出共性问题,而不是每天疲于奔命。
3.3 真正关键的是“数据契约”,不是血缘
血缘(lineage)确实是DataOps里的重要信息,但依赖血缘系统解决所有问题是很多团队的误区。血缘工具画出来的链路图再漂亮,如果它展示的是“字段从哪里来”,而不是“实际运行中数据是怎么流转的”,那它对排查问题的作用有限。
我在实际项目里更看重“数据契约”——数据生产者与数据消费者之间的一份明确约定。契约的内容包括:
- 表/字段命名规范
- 字段的业务口径定义
- 字段的数据类型、取值范围、编码标准
- 数据产出的时限(SLA,比如“每日凌晨6点前必须产出”)
- 数据质量的最低标准(非空率、唯一性、变更率容忍范围)
有了契约,数据管道之间就不再是“你产出、我用”这种模糊关系,而是有明确检查点的协作关系。上游要保证产出符合契约,下游消费前可以自动校验契约是否符合要求。这份契约甚至可以用机器可读的方式维护,比如用YAML文件定义在仓库里,在数据发布时自动校验。
我印象很深的一个案例:业务团队要上线一个新功能,改了订单表里的一个字段含义,从“下单时间”改成“支付完成时间”。这个改动在业务代码里只影响一个页面展示,但在数据链路里影响的是无数下游报表和模型。没有数据契约,这个改动可能要过两三周才会以“报表数据对不上”的方式爆发出来;有了契约,发布前就能被自动检查拦截:上游字段口径变更了,旧契约校验失败,下游负责人提前收到变更通知,该调整的调整,该确认的确认。这才是DataOps要解决的核心问题——不是让所有数据任务永远不失败,而是让变更、失败、恢复都是可控和可预期的。
4. 支撑这套流程的工具链,选型不翻车的几条经验
4.1 调度与编排:别只盯着Airflow
编排工具是数据流水线的骨架,Airflow因为生态成熟、社区样本多,确实是最多人上手的方案。但Airflow的坑也明显——它本质上是一个“调度+平台”性质的系统,DAG写起来灵活,但复杂依赖一多,多层依赖关系和动态任务生成会让DAG的可读性迅速恶化。我自己维护过一个几百个任务的大规模Airflow集群,到了后期,出问题最多的已经不是任务逻辑本身,而是DAG之间的依赖关系被隐式逻辑搞乱。
如果你在选型,我给一个比较务实的判断维度:
| 需求特征 | 推荐方向 | 理由 |
|---|---|---|
| 团队有Python基础,任务类型以SQL/Spark为主,需要高度定制 | Airflow | 生态最全、踩坑资料最多、定制度高 |
| 数据资产丰富,任务间依赖复杂,想把数据资产和任务放在一起管理 | Dagster | 软件定义资产模型,资产与任务绑定关系清晰,可观测性强 |
| 团队规模小,任务量不大,希望上手快、开发体验现代化 | Prefect | API设计友好,本地开发和云端执行分离做得好 |
| 深度绑定云厂商,不想自己运维调度平台 | 云厂商托管调度服务(如AWS MWAA、阿里云DataWorks等) | 运维成本最低,但厂商锁定要提前评估 |
我自己现在的项目用的是Dagster。原因是我们的业务已经不缺任务,缺的是“看清任务和资产的关系”。Dagster的Asset模型让我可以像声明变量一样把数据资产声明出来,任务之间的关系通过资产依赖自然表达,调度、血缘、日志都围绕资产展开,排障路径清晰很多。但这不意味着Airflow不好——如果你的团队已经熟练使用Airflow,迁移成本是需要认真衡量的,工具永远是为流程服务的,不是反过来。
4.2 数据质量测试:不是装一个平台就完事
数据质量是DataOps里最容易被“形式化”的部分。很多团队选型了商业数据质量平台,配置了一堆质量规则,但告警一封封发出来,处理率却趋近于零。原因不外乎两个:规则阈值设得过于敏感,或者质量规则和具体任务脱节,告警发出去了没人知道要找谁。
我的经验是先用轻量级工具把流程跑通,再考虑要不要上重平台。开源领域比较典型的两个选项是Great Expectations(以下简称GE)和Soda Core。
GE适合做“数据文档式”的验证:通过Expectation Suite描述“我期望这份数据长什么样”,然后对DataFrame或数据库表执行验证。它和Notebook、Python生态结合得很好,适合数据探索阶段快速做质量断言。
Soda Core对“数据管道里的自动监控”更友好,可以直接在配置里声明数据源的检查规则,比如“昨日订单表行数不能少于100万”“customer_id列唯一值数量偏差不能超过5%”,和CI/CD集成很顺滑。它生成的告警信息也更贴近工程侧需要。
我自己现在的做法是:GE负责开发和测试阶段的数据验证,Soda Core负责生产环境的监控。测试阶段把关的是“逻辑写对了没”,生产监控管的是“今天的真实数据出了什么幺蛾子”。两件事的语义不同,用两种工具各管一段,反而比一个“万能平台”更顺手。
4.3 元数据与血缘:先明确要解决什么问题,再选工具
血缘工具的选型,也是很多团队会纠结的地方。我的建议是:先想清楚你要血缘解决什么问题,再选工具。
如果要解决的是“合规审计”场景——某个字段的数据来自哪些系统,谁加工过,展示给谁看过,那需要的是能够从SQL解析出溯源关系的血缘工具,OpenMetadata或者DataHub都可行,但关键是你的SQL和任务是不是都规范化管理了。如果SQL本身就散落在各个临时脚本里,血缘工具再强也画不出完整的依赖图。
如果要解决的是“排障场景”——某个表的数据怎么变了,影响了下游哪些表和报表,那血缘必须和实际调度运行记录结合起来。静态SQL解析出的血缘经常漏掉动态生成SQL的场景,比如通过配置文件拼接出来的表名。排障的时候指着血缘图说“这条链路有影响”,结果实际动态运行时走了另一条链路,反而误导排查方向。
我踩过的一个具体坑是:早期我们实现血缘是拿SQL文本做正则解析,覆盖率看起来还行,但后来发现有一个核心表的血缘关系解析错了——因为系统在SQL里用了变量替换表名,静态解析识别出来的上下游全错了,排障时沿着错误血缘查了半天,才发现问题出在真正的上游。后来我把血缘的维护方式改成了基于实际运行记录的采集(从任务调度系统的执行日志反推真实的上下游关系),正确率才真正上来。这个经验一句话总结就是:血缘要吃“实际运行记录”,不能只吃“声明式依赖”。
4.4 数据版本的哲学:区分“临时弹性”和“长期可复现”
在DataOps里有一个被低估的问题:数据管道跑出来的结果能不能复现。代码有版本号,数据同样应该有可追溯的信息——这份数据是哪个任务、哪个版本、哪个时间窗口跑出来的。
我在设计数据任务时,坚持一个原则:核心业务数据表的产出信息必须自描述。最简单的方式是给表增加一套“元数据字段”,不管叫_snapshot_time,_job_version,还是_sql_commit_id,关键是让每一个下游消费者能回答“这份数据是什么时候生成的、由哪个版本的任务加工出来的”。
这个原则在排查历史数据质量问题时特别管用。比如业务方某天来问“为什么上周三的报表数据和今天重刷的数据对不上”,如果你有版本信息,很快可以定位是“上周三跑的是旧版任务,今天跑的是新逻辑”,从而快速判断差异原因。如果没有这些字段,就只能靠猜,而数据领域最怕的就是靠猜。
数据版本还有一个维度是回放/重放。数据管道重跑一个历史分区,应该像代码回滚一样可预期。我建议每个调度任务都支持“指定业务日期范围重跑”的能力,且重跑时必须校验已有分区是否会被覆盖、下游是否感知到数据已变更。这一点看起来基础,但在真实项目里,因为重跑导致下游重复计算、重复入数的案例比比皆是。
5. 最容易翻车的三个环节,修复记录
5.1 数据回滚:为什么“回滚”这个词在数据领域是伪命题
做DataOps的过程中,我遇到过最棘手的一次事故:上游业务系统的库表结构变更,导致当天清洗任务产出的订单数据出现了大面积乱码和字段错位。事故发现已经是业务部门下午看报表的时候了。
第一反应是“回滚”——把任务切回昨天的版本重新跑。但紧接着问题来了:今天的表已经被昨天的版本覆盖,但下游的报表已经在早上读了新数据,算法团队已经用今天的错误数据跑了一版模型。回滚任务能修复表,但修复不了已经发生的消费行为。
那次之后,我把数据任务事故响应流程改成了三步走:
- 止损:第一时间停掉下游所有依赖这条链路的任务,防止错误数据扩散。
- 修复数据:用正确的逻辑重新生成受影响分区,同时把数据变更事件主动通知给所有下游消费者,附上“数据已修正,请基于新数据重新消费”的说明。
- 复盘根因:为什么上游变更没有被及时发现?是测试数据集没有覆盖到这个变更场景,还是监控阈值没有覆盖范围校验(values beyond expected range)?
数据回滚的真相是:你无法让所有消费方回到事故发生之前,你能做的是快速缩短“错误数据的生命周期”,并让错误数据的扩散范围尽可能小。这种思路应该被设计进系统里,而不是等事故发生时靠人工临时抱佛脚。
5.2 测试的“数据保鲜”:离线验证通过,上线就挂
做数据管道自动化测试时,另一个高频翻车点:开发和测试阶段用的是历史样本数据,验证全过,一上线跑真实数据,直接挂掉。
原因基本都是测试数据和真实数据分布差异过大。比如开发环境里的测试表数据量是几千行,生产环境每天产出上亿行;测试数据里的枚举值只有两三个,生产环境里能冒出十几种异常编码。
我的修复方案是“影子测试+数据子集”的组合打法:
- 影子测试:在测试环境里,用一份脱敏后的生产数据子集(按核心维度抽样)来运行任务。这样测试的输入和生产的输入分布基本一致,跑出来的结果才有说服力。
- 数据子集:从生产数据中按规则抽取一个“小而全”的分区集。抽取规则不是随机抽样,而是“每个分支机构取一天数据、每个业务类型取一条极端值记录、每种异常码保留一条”,确保测试集能覆盖生产数据的主要分布特征。
另外,质量测试里一定要包含“分布变化检测”这一项。不一定用太重型的统计检验,一个简单的移动窗口均值/方差对比就很有用:计算昨日核心指标均值与近7日均值的偏离度,超过阈值就告警。这种检测对“数据突然漂移”类问题极其有效。
5.3 血缘优先级的教训:静态解析必须让位于运行时信息
我在4.3里提过静态血缘解析的坑,这里把排查链路完整记录一遍。当时的情况是:业务方反馈“某核心报表的今日数据比昨日少了30%”,我们需要快速定位“少的数据源头在哪”。
第一轮排查,我们打开血缘系统,看到“报表→DW层某汇总表→ODS层订单表”这条依赖链,检查了链路里的每一个任务,都没发现问题,任务全部成功、调度全部正常。数据为什么还少?
第二轮排查,我们把视角从“血缘图”切到“实际运行日志”,去调度系统里查这个报表任务真正依赖的上游节点。结果发现,该报表任务在调度配置里除了血缘图上显示的那张汇总表,还依赖一个“手工同步任务”产出的外部表,这张外部表是另一个团队通过一个临时脚本同步的,血缘系统完全没有采到。昨天这个手工任务因为源端权限变更跑失败了,没人注意到。
那一刻我彻底明白了:血缘工具只是一个辅助,真正给出事实的是系统的运行记录和任务依赖关系。血缘图可以帮你做信息检索和审计,但排障时必须把“实际执行链路”作为第一依据,血缘可视化只能作为辅助理解。后来我们强制要求所有任务在调度系统中的依赖都必须显式声明,任何隐式依赖(比如任务里面用代码读取另一张表,但没有在调度层声明)都要被消灭,同时血缘展示的优先级改为“运行记录优先、静态解析兜底”。这套调整之后,同类排障的时间缩短了很多。
6. 如果你也想从0开始跑DataOps,第一步应该这么做
每次有人问我“我们团队也想做DataOps,应该从哪里开始”,我的回答都不是“买工具”,而是“先选一条具体的数据链路,把它当样板间来做”。
不要一开始就试图做全平台改造,那样会陷入第二章节说过的“工具幻觉”。我的建议是:选择一个“高频使用、常出问题、影响面可控”的核心业务数据域,比如订单域或者用户域,把这一条链路的端到端流程彻底理一遍。
具体路径可以按四步走:
- 找出这条链路的所有数据任务、责任人、依赖关系和消费方,压缩成一张真实的链路清单。
- 在任务的上游与下游之间建立明确的数据契约,把字段口径、SLA、质量预期都白纸黑字定下来。
- 给这条链路套上自动化质量检测和告警闭环:数据产出后立即跑质量检查,失败立即告警并自动暂停下游。
- 用一周的时间,把这条链路的失败任务和告警做一次复盘,找到最高频的问题根因。
这个“样板间”做好之后,你会得到三个可以量化的收益:
- 管道失败率明显下降(因为失败被更快发现、更快处理)
- 数据交付时间变得可预期(因为有了SLA和数据契约)
- 团队积累了完整的DataOps实践方法(而不是一堆工具的说明书)
再把样板间的经验复制到其他数据域,推进阻力会小很多,因为拿出来的都是真实的成功案例,而不是一套纸面方法论。
我在自己的项目里就是这么起步的。最初只是选了订单域的一条核心链路做了改造,用了大概三周时间把链路理清楚、加了契约和质量检测、砍掉了两个无人认领的下游任务。三周之后的效果是:这条链路的平均失败恢复时间从“小时级”降到了“分钟级”,因为告警能直接定位到具体的任务和环节,不再需要人肉翻日志。后面再往其他域扩展时,团队里没有人再质疑DataOps是不是一个空洞的概念,因为大家已经看到了它带来的实实在在的变化。