简介:《ETL设计详解(数据抽取、清洗与转换)》是一份面向BI与数据仓库初学者、ETL设计人员的专题文档,围绕数据抽取、清洗转换与加载三条主线,帮助读者建立可落地的ETL设计框架。全文按抽取、清洗、加载展开:抽取部分讨论业务系统DBMS调研、ODBC链接、文件数据导入及基于时间戳的增量更新;清洗部分区分不完整、错误与重复数据,并说明过滤、修正和确认流程;转换部分涉及编码统一、粒度聚合与业务规则计算,同时补充日志记录和警告发送思路。包内共1个docx文档,压缩包约20KB,篇幅紧凑,适合作为方案设计或项目复盘时的速查材料。目前已有1538人学习,适合需要快速梳理ETL全流程、补齐数据质量处理细节的读者参考。
1. 月底报表对不上时,问题往往不在 SQL
一个很常见的场面:月底经营分析会,财务算出来的营收和报表上的营收差了三万块。两边都认为自己没错,一路翻到最底层才发现,是同一家供应商在两个业务系统里的编码不一样——结算系统里叫 XX0001,客户系统里叫 YY0001,ETL 抽过来没有做统一,聚合时就被拆成了两家。问题不在某条 SQL 写错,而在 ETL 设计这一层没有立住。
这套 ETL 设计详解要解决的就是这条链路:数据从几个业务系统抽到 ODS,怎么做数据抽取、怎么清洗脏数据、怎么转换编码与粒度、怎么加载进 DW,日志怎么记、出错怎么告警、上线怎么验收。它适合正在做数据仓库课程设计、BI 方向毕设的学生,也适合要交一份数据集成方案的在职开发。整套设计按抽取、清洗与转换、加载三段推进,中间给到可直接改参数落地的表结构与 SQL。
2. 总体架构设计:ODS 与 DW 两层职责与三种实现路线的取舍
很多方案一上来就写「用某某工具做 ETL」,写完发现没法解释为什么这么选。真正决定后面所有工作量的,是先把分层划清楚,再决定抽取、清洗、转换分别落在哪一层、用什么手段实现。这一章把这两件事定下来,后面的抽取和清洗才有地方放。
2.1 两层数据流:从业务库到 ODS 再到 DW 的职责边界
数据仓库通常分 ODS 和 DW 两层,这个划分不是为了好看,而是为了让「脏」和「净」分开。业务系统到 ODS 这一段做的是清洗——把不完整、错误、重复的数据挡在外面或者打上标记;ODS 到 DW 这一段做的是转换——统一编码、把明细聚合成分析粒度、把业务指标算好存下来。
这样分的好处是责任清晰:ODS 里的数据允许脏,但每条脏数据都能查到来源和拒绝原因;DW 里的数据必须是干净的、口径统一的,任何一条记录都能追溯到 ODS 的批次号。我一般会在 ODS 每张表上强制加三个字段:batch_id(批次号)、src_system(来源系统标识)、load_time(落地时间)。后面排查「这个数是从哪来的」全靠这三个字段。
分层还有一个现实收益:清洗规则改了,只需要重跑 ODS 到 DW 这一段,不用回头再动业务库。业务库是别人的地盘,动一次要走一次协调流程,能不碰就不碰。
2.2 三种实现路线:图形化工具、纯 SQL、混合
ETL 的实现常见三条路。第一条是借助图形化 ETL 工具,拖拽组件建工程,能快速把流程搭起来,把复杂的编码任务屏蔽掉,缺点是灵活性差,遇到奇怪的业务规则经常要靠自定义脚本绕;第二条是纯 SQL 实现,灵活、可控、跑得快,缺点是编码量大,对写的人要求高,流程编排、日志、告警都得自己造;第三条是工具加 SQL 混合,用工具管调度、依赖、日志和告警,用 SQL 做真正的数据处理。
我一般选第三条。理由很实际:调度和日志这类「脏活」自己写一遍要花掉两三周,而且很难写得比成熟工具稳;但数据处理逻辑写成 SQL,改起来最快,性能也最容易调。如果一个项目里清洗转换规则超过二十条,纯工具拖拽的维护成本会迅速超过手写 SQL 的成本。
选型还要看团队。如果接手的人 SQL 功底一般,硬上纯 SQL 方案,后期改一条规则就要翻半天代码;反过来,如果团队里没人会写窗口函数和复杂 join,纯工具也撑不住复杂的粒度转换。选型不是选最好的,是选团队能维护到上线后半年的。
提示:无论走哪条路线,清洗和转换逻辑都必须能落到可读的文本(SQL 脚本或配置文件)里,只存在于工具图形界面里的规则,交接时就是黑匣子。
2.3 开工前的调研清单:四个问题不问清就要返工
数据抽取在设计之前需要在调研阶段做大量工作。有几个问题必须在动手前问清楚,问不清后面全是返工:数据从几个业务系统来,各系统的数据库类型是什么,是否存在手工数据、量有多大,是否存在非结构化数据。
前两个问题决定抽取通道怎么设计——同构源可以直连,异构源要走中间落地;第三个问题决定清洗的复杂度,手工录入的数据几乎必然带全角字符、多余空格、日期格式混乱;第四个问题决定这一期要不要做,很多项目在调研阶段才发现有一批扫描件和聊天记录被算进了「数据源」,实际上根本没法结构化处理,硬做只会拖垮进度。
除了这四个问题,还要顺手确认三件事:源表有没有可靠的更新时间字段(决定能不能做增量)、源表的数据量级(决定要不要分批)、源系统有没有只读从库(决定抽取会不会影响业务)。这三件事在方案评审时经常被忽略,上线第一个跑批就出问题。
2.4 命名、批次与目录约定
约定比技术更能决定一个 ETL 工程能不能活下去。表命名我一般用「层前缀_主题_粒度」的结构,比如ods_order_src、dw_fact_customer_day,中间表和临时表统一加tmp_前缀并带批次号后缀,跑完即删。脚本按「层/主题/步骤号」组织目录,步骤号两位数补零,保证文件排序就是执行顺序。
批次号是贯穿全流程的主线,格式通常是yyyyMMddHHmm加两位序号,每次调度生成一个,写进所有落地表的batch_id字段。好处是重跑时只要指定批次号,就能把这一批的数据整体回滚再重来,不用一条条找。这套约定看着琐碎,但它是后面日志、告警、验收全部能对上的前提。
3. 数据抽取设计:四类数据源通道与增量水位线
抽取阶段最容易被低估。很多方案写一句「从业务系统抽取数据到 ODS」就过去了,实际做的时候,同构源、异构源、文件源、手工数据这四类通道的处理方式完全不同,增量还是全量更是直接决定跑批能不能在天亮前结束。
3.1 同构源直连与异构源的两条落地路径
和 DW 同类型的数据源最好处理。数据库自带链接功能,在 DW 服务器和业务系统之间建一个直接的访问通道,写 select 就能读,不需要中间文件。这种情况下抽取脚本本质就是一条带条件的查询加一次插入,简单、可控、出错点少。
异构源麻烦一些。类型不同的两个库之间一般也能通过 ODBC 建立链接,但性能和稳定性都要打折,尤其是跨库大批量拉取的时候。建不了链接的情况有两条路:一条是用工具把源数据导出成文本或表格文件,再把这些文件导入 ODS;另一条是走程序接口,由源系统提供数据出口。
选择哪条,看数据量和使用频率。一次性迁移,导出文件中转最快;每天都要跑的固定通道,值得花时间把接口或直连链路做稳。用文件中转做日常通道是典型的踩坑做法——文件落地目录没人管,某天源系统改了导出格式,ETL 静默跑成功,数据全是空的。
3.2 文件类数据源的落地位置与责任划分
文本和表格类文件在项目里几乎一定存在,来源往往是业务人员手工整理或者第三方定期推送。这类数据的处理原则是:先落到数据库,再从数据库抽取,不要让 ETL 直接读业务人员电脑上的文件。
通常的做法是培训业务人员用数据库工具把这些数据导入到指定的库表,或者给一个固定的共享目录,由 ETL 定时扫描目录、校验文件命名和列结构、导入 ODS 的落地表。落地表建议全部用字符串类型,先接进来再转换,不要指望源文件里的「金额」列真的是数字——带千分位、带货币符号、带空格的情况太常见了。
责任划分要写进方案里:文件由谁提供、什么时间提供、格式变更提前多久通知、文件缺失时 ETL 是报错还是跳过。这几条不写清楚,后期每次数据对不上都要开一次会。
3.3 增量抽取:时间戳水位线与边界重复的处理
数据量大的系统必须做增量,全量抽取跑一次要几小时,业务根本等不起。最常见的增量标志是业务系统记录的时间,每次抽取前先取 ODS 中已有的最大时间,再去源系统取大于这个时间的记录。
-- 增量抽取:以源表 update_time 为水位线,把增量落到 ODS INSERT INTO ods.order_src (order_id, customer_id, amount, order_time, update_time, batch_id, src_system, load_time) SELECT s.order_id, s.customer_id, s.amount, s.order_time, s.update_time, :batch_id, -- 本次调度生成的批次号 'SYS_A', -- 来源系统标识,多源合并时靠它区分 CURRENT_TIMESTAMP FROM src.orders s JOIN etl.watermark w ON w.table_name = 'orders' WHERE s.update_time > w.last_value -- 开区间:上批已处理的不再重复拉 AND s.update_time <= :batch_end_time -- 本批上界:调度启动时刻,防止边界漂移这段的关键在两处。第一处是区间取了左开右闭:>上批水位、<=本批上界。如果写成>=,同一秒内产生的多条记录会在两批里各出现一次,DW 里的金额就会翻倍,这是最典型的踩坑。第二处是上界用的是调度启动时刻而不是CURRENT_TIMESTAMP,因为跑批过程中新产生的数据应该留给下一批,否则一批数据跨了两个时间点,重跑时对不上。
跑完之后必须回写水位线,而且要和主流程在同一个事务里,主流程失败水位线不推进:
-- 抽取成功后推进水位线,失败时整体回滚 UPDATE etl.watermark SET last_value = :batch_end_time, update_time = CURRENT_TIMESTAMP WHERE table_name = 'orders';还有一种情况要提前处理:业务系统根本没有可靠的更新时间字段,或者字段只在部分表上有。这时只能用全量比对——把源表整表拉进临时表,和 ODS 做一次全字段比对,只写有差异的记录。代价是每次都全量读,但至少结果是准的。更差的做法是拿自增主键当增量标志,一旦有历史数据被修改,主键不变,改动永远抽不过来。
3.4 抽取性能:分批、限流与错峰
抽取跑得慢,八成是两个原因:一次性拉太多,或者和业务高峰撞上了。分批的做法是按主键或时间切片,每批几千到几万行,批间留一点间隔,避免长时间占用源库的连接和 IO。切片字段要选有索引的,否则每批都是一次全表扫描。
限流是给源库留余地。抽取任务本质上是在别人的生产库上做只读操作,跑满连接数会把业务拖慢,这个责任担不起。我一般会把并发控制在个位数,并且明确要求走只读从库;没有从库的,跑批时间挪到凌晨业务低谷。
错峰还包括和同项目的其他任务错开。多个抽取任务同时启动,目标库的写入会成为瓶颈,日志表也会锁等待。调度上给不同主题设置不同的启动时间,间隔十分钟,比调优 SQL 见效快。
4. 数据清洗与转换:三类脏数据分流与转换规则落地
ETL 三个部分里,花时间最长的是清洗和转换,一般占到整个 ETL 工作量的三分之二。这部分做得好不好,直接决定 DW 里的数据能不能拿去分析。设计上要解决两个问题:脏数据怎么分流,业务规则怎么变成可执行的转换。
4.1 清洗流水线:三类脏数据的分流与回流
不符合要求的数据主要有三类:不完整的数据、错误的数据、重复的数据。清洗的第一步不是删,而是分流——把有问题的记录挑出来,打上原因标签,写进拒绝表,同时保留原始值。
不完整的数据特征是应有的信息缺失,比如供应商名称空着、客户所属区域缺失、主表和明细表匹配不上。这类数据不能直接丢,通常的做法是按缺失字段分类,分别导出成表格交给业务部门,要求在规定时间内补全,补全之后再重新抽取。丢掉不完整数据的后果是月底对账时金额差一截,而且没人知道差在哪。
分流的关键是拒绝表要包含原始记录的全部字段加上拒绝原因和批次号。这样业务方拿到表格能直接看出缺什么,开发方重跑时也能按批次号精确补数。
4.2 错误数据的识别 SQL 与修正闭环
错误数据大多是业务系统输入校验不严造成的:数值被输成全角字符、字符串尾部带了回车或空格、日期格式不对、日期超出合理范围。这一类要再细分,因为处理方式完全不同。
能自动修正的(去空格、全角转半角、常见日期格式归一)在 ODS 层直接修掉,并记一条修正日志;修不了的(日期越界、编码不存在)必须回到源系统修正后再抽,因为 ETL 强行猜一个值,比留着空更危险。
-- 错误数据筛查:给每条异常记录打上原因标签,写入拒绝表 INSERT INTO ods.order_reject (order_id, amount_raw, order_time, reject_reason, batch_id) SELECT order_id, amount_raw, order_time, CASE WHEN order_time IS NULL THEN 'MISSING_DATE' WHEN order_time < '2000-01-01' OR order_time > CURRENT_DATE THEN 'DATE_OUT_OF_RANGE' WHEN amount_raw <> TRIM(amount_raw) THEN 'TRAILING_SPACE' WHEN amount_raw ~ '[0-9]' THEN 'FULLWIDTH_DIGIT' WHEN amount_raw !~ '^-?[0-9]+(\.[0-9]{1,2})?$' THEN 'BAD_NUMBER' ELSE 'UNKNOWN' END AS reject_reason, :batch_id FROM ods.order_src WHERE order_time IS NULL OR order_time < '2000-01-01' OR order_time > CURRENT_DATE OR amount_raw <> TRIM(amount_raw) OR amount_raw ~ '[0-9]' OR amount_raw !~ '^-?[0-9]+(\.[0-9]{1,2})?$';这段的逻辑是先用 where 把可疑记录筛出来,再用 case 打标签,一行记录只打一个主要原因。正则部分要按目标库的方言调整,示例里的~是 PostgreSQL 系写法,换成别的库可能要改成REGEXP_LIKE。参数上,2000-01-01是业务合理下界,实际项目里按最早业务年份设;金额正则限制两位小数,超过的进拒绝表而不是四舍五入,因为金额精度不能靠猜。
拒绝表要有人看。开发初期可以每天把过滤结果导出,发给业务部门,一来推动他们尽快修数据,二来这些文件以后就是验证数据准确性的依据。
4.3 重复数据:维表去重与事实表幂等
重复数据在维表里最常见。同一家供应商因为录入两次,出现两条编码不同但名称相同的记录;同一个客户在不同系统里各有一份档案。处理方式是把重复记录的所有字段导出来,交给业务方确认保留哪一条、怎么合并,确认之后再写映射关系。
事实表的重复是另一个问题,通常由重跑引起。解决办法是让加载具备幂等性:写入前先按batch_id删除同批次数据,再整批插入;或者用主键加业务日期的唯一约束,重复插入直接失败。前者更常用,因为重跑时能保证结果和首次运行一致。
去重不能在 ETL 里靠DISTINCT一把梭。DISTINCT会把「同一客户的两笔不同订单」也压成一条,这是血泪经验。去重的前提是明确业务主键,按主键分组后用窗口函数排序取最新一条:
-- 维表去重:按业务主键取 update_time 最新的一条,其余打上重复标记 SELECT * FROM ( SELECT supplier_code, supplier_name, update_time, ROW_NUMBER() OVER ( PARTITION BY supplier_code -- 业务主键,不是物理主键 ORDER BY update_time DESC, load_time DESC ) AS rn FROM ods.supplier_src WHERE batch_id = :batch_id ) t WHERE rn = 1;PARTITION BY后面跟的必须是业务主键,跟错了去重就是错的;ORDER BY的排序字段决定保留哪条,一般用更新时间倒序,时间相同再按落地时间兜底。取rn = 1得到干净维表,rn > 1的记录写进重复表供业务确认。
4.4 转换三件事:编码统一、粒度聚合与商务规则计算
数据转换主要做三件事。第一件是不一致数据转换,把不同业务系统里同一实体的不同编码统一成一个,比如供应商在结算系统是 XX0001、在客户系统是 YY0001,抽过来之后统一映射成一个主键。这一步必须靠一张人工维护的映射表,不能靠名称模糊匹配。
-- 编码统一:源系统编码经映射表转成统一主键,映射不到的打 UNK 前缀保留 INSERT INTO dw.dim_supplier (supplier_key, supplier_name, src_system, src_code, batch_id) SELECT COALESCE(m.unified_id, CONCAT('UNK_', s.src_system, '_', s.supplier_code)) AS supplier_key, s.supplier_name, s.src_system, s.supplier_code, :batch_id FROM ods.supplier_src s LEFT JOIN etl.map_supplier m ON m.src_system = s.src_system AND m.src_code = s.supplier_code WHERE s.batch_id = :batch_id;COALESCE的作用是映射不到时给一个可识别的兜底值,UNK_前缀让这批记录在报表里一眼能看出来,比直接丢掉或者给 NULL 强得多——NULL 会在关联时静默消失,兜底值至少能被统计到。
第二件是粒度转换。业务系统存的是明细,数据仓库做分析不需要那么细,通常按分析维度聚合。第三件是商务规则计算,不同企业的指标口径不一样,很多指标不是简单加减能算出来的,需要在 ETL 里算好存下来。
-- 粒度转换 + 商务规则:明细订单聚合到「客户 + 日期」粒度,并折算金额 INSERT INTO dw.fact_customer_day (customer_key, stat_date, order_cnt, gmv, gmv_adjusted, batch_id) SELECT d.customer_key, CAST(o.order_time AS DATE) AS stat_date, COUNT(DISTINCT o.order_id) AS order_cnt, SUM(o.amount) AS gmv, SUM(o.amount * COALESCE(r.rate, 1)) AS gmv_adjusted, -- 商务规则:按当日汇率折算 :batch_id FROM ods.order_src o JOIN dw.dim_customer d ON d.src_customer_id = o.customer_id LEFT JOIN etl.rate r ON r.currency = o.currency AND r.stat_date = CAST(o.order_time AS DATE) WHERE o.batch_id = :batch_id GROUP BY d.customer_key, CAST(o.order_time AS DATE);这段里COUNT(DISTINCT order_id)是必须的,因为明细表可能因为关联维表产生重复行,不去重订单数会虚高;COALESCE(r.rate, 1)保证汇率缺失时不至于整行变 NULL,但这类缺失要单独出告警,不能默认吞掉。聚合粒度一旦定下来就是对外承诺,改粒度等于改所有下游报表,方案里要写清楚并且让使用方签字确认。
4.5 清洗规则的验证:别把有用数据过滤掉
清洗最难的不是写规则,是确认规则没有误杀。每一条过滤规则上线前都要验证:拿一批历史数据跑一遍,统计被过滤掉的记录数和占比,人工抽查一部分,确认确实是不该进来的数据。
验证还有一个技巧,是反向验证——用过滤前的数据和过滤后的数据分别跑一次核心报表,看差异是否在可解释范围内。如果差异突然变大,说明新规则误杀了一批正常数据。这类问题在测试环境不容易暴露,因为测试数据往往太干净。
每一条规则都要有明确的负责人和确认记录。规则是业务方确认的,不是开发自己定的,这一点在验收和答辩时经常被追问,说不出规则依据的方案很难过关。
5. 日志告警与运行期排查:三类日志表与五个翻车点
ETL 上线之后,绝大多数时间不是在开发,是在排查。跑批失败、数据不对、跑得越来越慢,这些问题如果没有日志,就只能靠猜。日志设计的标准很简单:任何一次异常,都能在五分钟内定位到哪个批次、哪一步、哪条记录。
5.1 三类日志表的结构设计
ETL 日志分三类。执行过程日志记录每一步的起止时间、影响行数,是流水账;错误日志只在模块出错时写,记录出错时间、模块、错误信息和样本主键;总体日志只记一次调度的开始时间、结束时间和成功与否。三类分开建表,查询时才不会互相干扰。
-- 总体日志:一次调度一条,回答「昨晚跑成功了吗」 CREATE TABLE etl.job_log ( batch_id VARCHAR(32) PRIMARY KEY, job_name VARCHAR(64), start_time TIMESTAMP, end_time TIMESTAMP, status VARCHAR(8), -- RUNNING / SUCCESS / FAILED row_affected BIGINT ); -- 执行过程日志:一步一条,回答「卡在哪一步、慢在哪一步」 CREATE TABLE etl.step_log ( batch_id VARCHAR(32), step_no INT, step_name VARCHAR(64), start_time TIMESTAMP, end_time TIMESTAMP, row_read BIGINT, row_written BIGINT, status VARCHAR(8) ); -- 错误日志:只记异常,回答「错在哪条数据上」 CREATE TABLE etl.error_log ( batch_id VARCHAR(32), step_name VARCHAR(64), err_time TIMESTAMP, err_code VARCHAR(32), err_msg TEXT, sample_key VARCHAR(64) -- 出错记录的业务主键,方便回查原始数据 );三张表的分工要守住:job_log一行代表一次调度,用于对外汇报和告警触发;step_log用于性能分析,row_read和row_written差距过大通常意味着过滤条件写错了;error_log里的sample_key是最值钱的字段,没有它,报错信息只有一句「数据类型转换失败」,根本不知道是哪条数据。
如果用了图形化 ETL 工具,工具本身会产出一部分日志,这些日志可以作为补充,但不要指望它覆盖全部。业务规则层面的失败(比如金额超出合理区间)工具是感知不到的,必须自己写。
5.2 告警发送与失败重跑
出错了不仅要写日志,还要主动通知。最常见的做法是给系统管理员发邮件,附上批次号、失败步骤和错误摘要。邮件内容要能直接用于判断,不要只发一句「ETL 失败」,收件人还得登录服务器翻日志。
告警要分级:跑批整体失败,立即发;单条数据被拒绝,攒到批后汇总发;跑批耗时超过阈值(比如比近七天平均多出 50%),也发一条。所有异常都实时发,收件人很快就会把告警邮件全部设成自动归档,等于没有告警。
重跑的设计要点是幂等。重跑前先按batch_id清理目标表里这一批的数据,再从头执行;水位线只在成功后推进,失败时保持原值。这样重跑三次和重跑一次的结果完全一致。没有幂等设计的重跑,跑两次数据就翻倍,这类问题在验收时一查一个准。
5.3 运行期最容易翻车的五个点
第一个:跑批成功但数据是空的。现象是job_log显示 SUCCESS,下游报表全是零。原因多半是抽取阶段用了文件中转,源文件当天没到,ETL 扫到零个文件也认为成功。解决办法是在抽取步骤加行数校验,row_read为 0 直接判定失败,同时给文件通道加上「文件未到则挂起并告警」,而不是静默通过。
第二个:金额翻倍。现象是某天的营收正好是前一天的 1.5 到 2 倍。原因通常是增量区间写成了闭区间,或者重跑前没清理同批次数据。解决是统一用左开右闭的时间区间,加载前按batch_id删除再插入,两条一起做。
第三个:跑批越来越慢。现象是第一个月跑 20 分钟,三个月后要跑 3 小时。原因一般是目标表没建索引、拒绝表和历史日志无限增长、或者每批都在全表扫。解决是按batch_id和业务日期建索引,日志表按月分区或定期归档,抽取切片字段必须有索引。
第四个:清洗规则误杀。现象是业务方反馈「某个客户的订单少了一批」,查下来是被某条过滤规则挡掉了。原因是规则写得太宽,比如日期下界设成了系统上线日,把历史补录数据全滤掉了。解决是每条规则上线前跑历史数据回归,统计过滤占比,超过阈值必须人工确认。
第五个:编码映射表没人维护。现象是新接入的供应商在报表里全部归到UNK_下。原因是映射表是人工维护的,新数据进来没有对应关系,却没有人负责更新。解决是把「未映射记录数」做成每日监控指标,超过阈值就推给数据管理员,而不是等报表出错才发现。
6. 验收与答辩:用一份可复现的清单说话
验收这一步,最忌讳的是拿一句「数据跑通了」交差。评审的人不会看你跑了多少条 SQL,他会问三个问题:数据准不准、出错能不能查、重跑会不会乱。我的做法是准备一份可以当场演示的验收清单,每一项都有对应的证据,而不是口头描述。
| 验收项 | 检查方法 | 合格标准 |
|---|---|---|
| 数据完整性 | 源表与 ODS 按批次比对行数 | 差额等于拒绝表记录数,且每类拒绝原因可解释 |
| 数据准确性 | 抽取三个核心指标手工复算 | 与 DW 结果一致,差异为 0 |
| 增量正确性 | 同一批次连续重跑两次 | 第二次结果与第一次完全一致,无重复无缺失 |
| 异常可追溯 | 人为制造一条日期越界数据 | 能从job_log追到step_log再到error_log的样本主键 |
| 告警可用 | 断开一个源连接后触发跑批 | 五分钟内收到含批次号和失败步骤的告警 |
| 性能达标 | 记录全量跑批耗时 | 在约定窗口内完成,且连续七天无显著增长 |
表格只是骨架,真正加分的是每一项背后的证据。数据准确性那一项,我会准备一张手工复算的表格,列出源系统原始值、ODS 值、DW 值三列,评审时直接摊开对照。增量正确性那一项,把两次重跑的job_log和step_log截图放在一起,行数一模一样,比任何解释都有说服力。
答辩时最常被问的是「你怎么保证清洗没有滤掉有用数据」。这个问题不要用「我们验证过了」回答,要用数字回答:总共过滤了多少条、占比多少、分几类原因、每一类抽查了多少条、业务方确认记录在哪里。我当时就是在这个问题上被追问了三轮,最后靠一张按拒绝原因分组的统计表才过。从那以后,我每次做清洗规则,都强制走一遍「历史数据回归 + 占比统计 + 业务确认签字」,哪怕项目再赶也不省这一步。
还有一个容易被忽略的点:答辩时不要只讲流程,要讲取舍。为什么选混合路线而不是纯工具,为什么增量用水位线而不是全量比对,为什么维表去重按业务主键而不是物理主键。能讲清取舍的方案,才像真的做过;只讲步骤的方案,一眼就是抄的。
最后留一个小习惯:方案交付时附一个「十分钟复现脚本」,从建表、灌测试数据到跑完一轮 ETL、导出验收报表,一条命令跑完。评审的人愿意自己跑一遍,比你在台上讲半小时管用。希望帮到你。
本文还有配套的精品资源,点击获取