简介:《ETL设计详解(数据抽取、清洗与转换)》是一份面向BI项目开发人员、数据仓库初学者与ETL实施者的技术文档,聚焦数据抽取、清洗转换与加载三个核心环节,帮助读者建立可落地的ETL设计框架。压缩包内仅含1个docx文件,大小约20KB,轻量便于查阅与归档。文档围绕数据源调研、抽取方式选择、增量更新、脏数据分类处理、粒度转换与商务规则计算展开,区分不完整、错误、重复数据的处理边界,并说明ETL工具、SQL及二者结合三种实现路径的适用差异,同时补充日志分类与警告发送思路,便于读者梳理从业务系统到ODS再到DW的处理链路。目前已有1538人学习,适合需要快速掌握ETL设计要点、对照检查项目方案或查漏补缺的数据从业者。
1. ETL 的三分之一时间账:为什么清洗转换永远是最贵的环节
做数据仓库项目的人大多有过这样的体感:抽取和加载写得飞快,真正把工期拖垮的是中间那段清洗转换。一个被反复引用的经验值是 ETL 整体吃掉 BI 项目三分之一的时间,而其中三分之二又压在 T 上。这个比例并不来自预算失误,它反映的是一个结构性事实——抽取解决的是「搬得动」,加载解决的是「放得下」,只有清洗转换解决「搬过来的东西能不能信」。数据源那边每多一种历史遗留的录入习惯,T 这一层就要多一套判定规则、一条分流路径和一条回滚方案。把这套 ETL 设计逻辑讲透,本质上就是讲清楚这些规则该放在哪一层、以什么形态固化下来、出错之后靠什么追溯。
2. 数据抽取:增量水位线为什么比事后比对更可靠
抽取这一层看上去最像体力活:连上源库、写个查询、把数据灌进 ODS 就完了。但真正决定后期返工量的,是抽取阶段定下的几个结构决策——连接方式、批次粒度、增量判定依据。这三件事一旦定错,后面无论清洗规则写得多细,都只能靠人工补数据来填坑。
2.1 数据源盘点:抽取方案在写第一行 SQL 之前就定了
抽取设计的第一步不是写代码,而是把源系统的底摸清。原文给了一份调研清单,我把它拆成五个真正影响设计的问题来看:
第一,数据来自几个业务系统。系统数量决定的是连接数和调度依赖图。三个源系统意味着至少三条独立的抽取链,任一条失败都不能让下游的汇总任务带着半份数据往下跑,所以调度上必须做成「全依赖就绪才触发汇总」。
第二,各个源系统跑的是什么类型的数据库。这决定了能不能直连、要不要落地文件中转。同一种数据库之间通常有原生链接能力,跨类型就得走标准连接驱动或者文件中转。
第三,有没有手工数据,量有多大。手工数据没有时间戳、没有主键约束、格式随人,往往需要单独设计一个「手工数据提交入口 + 校验窗口」,而不是混在自动抽取链里。
第四,有没有非结构化数据。日志、附件、报文这类数据需要先过一层解析才能进 ODS,解析失败的记录必须有单独的去处,不能直接丢弃。
第五,主键稳不稳定、更新频率如何。有些源表用的是业务编号做逻辑主键,中途会变更;有些表几乎不更新,有些表一天改几十万行。这两个信息直接决定增量策略该用水位线还是全量快照。
我一般会把这五个问题的答案写成一张源系统清单表,每行一个源表,列上包含库类型、主键、更新时间字段、日均增量、是否有手工补录。这张表后面会变成调度配置、清洗规则配置和容量评估的共同底稿。血泪经验是:跳过这一步直接开写,八成会在上线前两周发现某张关键表根本没有可靠的时间字段。
2.2 同构直连与异构落地:连接路径怎么选
连接路径大致分三类,选哪一类取决于源库和目标库的关系。
同构直连。源库和存放数据仓库的库属于同一种数据库产品时,最省事的做法是在两边建立数据库链接,直接写查询把数据拉过来。优点是实现快、无需中间存储、数据一致性好。代价是把查询压力直接打到了业务库上,尤其是大表全量拉取时,业务侧的慢查询告警会立刻找上门。常见的缓解手段是只读账号、错峰调度、按主键分批游标、限制单批行数。
异构中转。源库和目标库不是同一种产品时,通常通过标准数据库连接驱动建立通道。如果连通道都建不起来(网络隔离、权限审批、源库不允许外部连接),就退回到文件落地:把源数据导出成文本或表格文件,再导入 ODS 的临时表。文件落地看起来原始,但它有两个直连比不了的优点——文件可以被留存和重放,出了问题能拿当时的文件复现,这对排查对账差异极其重要。
文件型数据源。业务方直接给的表格或文本文件,两条路:一是培训业务人员用数据库工具把数据导入到一张约定的临时表,抽取流程只认这张表;二是用工具自带的平面文件组件做导入。前者的好处是责任边界清晰,脏数据的锅在源头;后者省人力,但解析规则要维护得很细,分隔符、编码、列顺序变化都会让流程静默错位。
这里有个容易被忽略的点:直连不等于高效。跨库直连做关联查询时,优化器往往拿不到另一侧的统计信息,执行计划可能退化成把整表拉回本地再关联。所以跨源关联尽量放在落地之后做,抽取阶段只做单表搬运。
2.3 增量抽取的时间戳水位线与它的三个失效场景
数据量一大,全量抽取就不可行,必须走增量。最常见的增量判定是时间戳水位线:每次抽取前先查出 ODS 中该源表已经落到的最大时间,然后去源库取大于该时间的所有记录。这个思路简单、可解释、实现成本低,原文也是这么描述的。
结构大致是这样:
-- 第一步:从 ODS 侧取水位线,注意用 coalesce 兜住首次运行的空值 -- 首次运行没有历史数据时,回退到一个足够早的基准时间 SELECT COALESCE(MAX(src_update_time), TIMESTAMP '1970-01-01 00:00:00') AS watermark FROM ods.order_detail; -- 第二步:用严格大于取增量,配合回看窗口防止边界丢失 -- :watermark 由上一段查询结果传入,:overlap 为回看分钟数(常见 30~120) SELECT * FROM src.order_detail WHERE src_update_time > :watermark - INTERVAL :overlap MINUTE AND src_update_time <= :batch_end_time; -- 固定上界,保证批次可重放代码里有三个需要解释的参数。watermark是上一次成功批次结束时的最大更新时间,它是整个增量机制的状态锚点。overlap是回看窗口,用「大于水位线减去一段回看时间」代替「严格大于水位线」,专门用来兜住那些时间戳被回填的迟到数据。batch_end_time是批次上界,必须显式固定,否则同一批数据在重跑时会因为时间漂移抽出不同结果,对账永远对不平。
这段逻辑有三个典型失效场景,每一个都能单独造成几万行数据缺口:
第一个是时间戳回填。业务系统里存在补录和修正操作,一条昨天发生的业务今天才写入,它携带的时间戳是业务发生时间而不是写入时间。如果增量只取「大于上次最大时间」,这条记录会永久丢失——因为下次抽取的水位线已经越过了它。回看窗口能救一部分,窗口大小取决于补录的最长滞后期,需要拿真实数据统计出来,而不是拍脑袋定 30 分钟。
第二个是时间戳不刷新。有些表的更新时间字段只在部分更新路径下才被赋值,某些存储过程直接改数据但不动时间戳。这种表用水位线抽会静默漏数据,而且不报错。检测办法是定期做行数与校验和的抽样比对,发现漂移就改用「更新时间 + 主键集合差集」的双重判定。
第三个是并发窗口跨越。抽取正在跑的时候源库还在写入,如果查询没有固定上界,同一行数据可能被抽两次,或者两次都没抽到。固定上界加幂等写入是唯一稳妥的解法。
写入端要配合做幂等合并,而不是简单插入:
-- 按主键做 upsert,保证同一主键无论重跑多少次,最终只有一行最新版本 MERGE INTO ods.order_detail AS t USING (SELECT * FROM stage.order_detail_batch) AS s ON t.order_id = s.order_id WHEN MATCHED AND s.src_update_time > t.src_update_time THEN UPDATE SET t.amount = s.amount, t.status = s.status, t.src_update_time = s.src_update_time, t.batch_id = s.batch_id WHEN NOT MATCHED THEN INSERT (order_id, amount, status, src_update_time, batch_id) VALUES (s.order_id, s.amount, s.status, s.src_update_time, s.batch_id);这里的WHEN MATCHED AND s.src_update_time > t.src_update_time条件很关键:加上它,旧版本数据就不会覆盖新版本;去掉它,重跑一个历史批次会把已经更新的行冲回旧值。batch_id记录的是最后一次改动该行的批次号,后面排查「这行数据是谁改的」时全靠它。
增量机制说到底是两件事:一是找到一个单调、可靠、不会倒退的判定依据,二是保证写入端幂等。判定依据选错只会漏数据,写入端不幂等则会错数据,后者更难发现。
3. 清洗与转换:脏数据为什么要先记账再拦截
抽取把数据搬进来之后,ODS 里躺着的往往是一份「如实记录了业务系统所有毛病」的副本。清洗和转换要做的不是把它变漂亮,而是把它变得可判定——哪些能用、哪些不能用、不能用的该怎么处置。这中间最容易做错的一步是「直接过滤掉」,因为过滤是不可逆操作,一旦规则写宽了,有效数据就没了。
3.1 三类脏数据的判定标准与分流去向
原文把不符合要求的数据分成三类,这个分法在实践中很好用,因为三类数据的处置责任方完全不同。
不完整的数据。特征是关键信息缺失:供应商名称空、分公司名称空、客户所属区域空,或者主表和明细表匹配不上。这类问题的根因通常在业务录入环节,而不是数据本身有问题,所以处置方式是把缺失项按字段分类导出成表格,提交给对应业务主管部门限期补全,补全之后再重新抽取。这里有个工程细节:分流表要按「缺失字段 + 责任部门」分组,一份大表丢过去没人认领,分到部门才有推动力。
错误的数据。产生原因是源系统接收输入时没做校验,脏值直接落了库。这类还要再分两小类,因为影响完全不同。一类是格式问题,比如数值字段里混进了全角字符、字符串尾部带不可见字符、编码不一致。这类数据不会让流程崩,但会让关联和聚合静默出错,需要单独写查询把它们挑出来,交给业务方在源系统修正。另一类是日期格式错误或日期越界,这类会直接让 ETL 任务报错中断,必须优先挑出来处理,否则整条链路卡住。
重复的数据。维表里最常见,同一个客户、同一个供应商存在多条记录。处置方式是把重复记录的全部字段导出来让业务方确认哪条是主记录、哪些需要合并,而不是由开发自行按「取最新一条」拍板——因为哪条正确只有业务知道。
分流这件事的正确做法是先记账再拦截。所有被规则判定为异常的记录,先写入一张异常明细表,带上批次号、规则编号、异常字段、原始行内容,然后再决定是否从主流程排除。
-- 异常明细表:所有被清洗规则拦下的记录都在这里留档 CREATE TABLE etl_reject_detail ( reject_id BIGINT PRIMARY KEY, -- 自增主键 batch_id VARCHAR(32) NOT NULL, -- 批次号,与日志表对齐 source_table VARCHAR(64) NOT NULL, -- 来源表名 rule_code VARCHAR(16) NOT NULL, -- 触发的清洗规则编号 reject_type VARCHAR(16) NOT NULL, -- INCOMPLETE / INVALID / DUPLICATE owner_dept VARCHAR(64), -- 责任部门,用于分流推送 row_payload TEXT NOT NULL, -- 原始行序列化内容,便于复现 reject_time TIMESTAMP NOT NULL );rule_code是这张表的灵魂。每条清洗规则一个编号,规则改了编号也升版,这样统计「某条规则一个月拦了多少行」才有意义,也才能判断是不是规则写太宽了。row_payload存原始行内容,是为了在业务方质疑「你凭什么说我这行是脏数据」时能当场还原。owner_dept支撑按部门分批推送。
这张表还有一个隐形价值:它是对账的依据。最终进仓库的行数 + 异常表里的行数 + 规则明确丢弃的行数,三者相加应该等于源表行数。对不上就说明中间有环节把数据弄丢了,而且没留痕。
原文特别提醒了一句「不要将有用的数据过滤掉了,对于每个过滤规则认真进行验证,并要用户确认才行」,这是清洗环节最贵的一条经验。规则上线前先跑一遍 dry-run,只写异常表不动主流程,把结果给业务方看三天,确认无误再打开拦截开关。
3.2 转换的三件事:编码归一、粒度聚合、指标预计算
清洗解决「能不能用」,转换解决「怎么用」。原文列了三类转换任务,我把它们的实现要点展开一下。
不一致数据的编码归一。同一个实体在不同业务系统里有不同的编码,比如同一个供应商在结算系统里的编号和客户关系系统里的编号完全不同。抽到 ODS 之后必须先统一成仓库内部的唯一编码,否则跨系统的关联分析根本做不了。实现上有两种常见做法:一是维护一张主数据映射表,源系统编码到标准编码一一对应;二是在转换逻辑里硬编码映射规则。前者可维护,后者写起来快但改一次要动代码。我一般会要求映射表带生效时间和失效时间,因为主数据合并是会发生的历史事实,用当前映射去翻译三年前的数据会得到错误结果。
数据粒度的转换。业务系统存的是明细,数据仓库面向分析,通常需要按天、按月、按组织维度做聚合。这里的关键是确定聚合的粒度和口径,并且把口径写成可复查的文档。同样是「日销售额」,含不含退货、含不含税、按订单创建时间还是支付时间,结果能差出两位数百分比。粒度的选择还会反向影响存储设计,聚合表的主键必须能唯一定位到一个聚合单元。
商务规则的计算。不同企业有不同指标定义,很多指标不是简单加减能算出来的,比如客户生命周期价值、毛利率的分摊、跨期费用的归属。这类计算放在 ETL 里预计算好存进仓库,还是留给 BI 层实时算,是一个明确的取舍:预计算快但改口径要重跑历史,实时算灵活但查询压力大。常见折中是核心指标预计算、探索性指标留给查询层。
转换逻辑还有一个硬要求:可重算。所有转换规则都应该能从 ODS 的原始数据重新跑出同样的结果,不依赖任何外部状态。做到这一点,口径变更时才敢说「重跑一遍历史就行」。
3.3 ODS 与 DW 的分层职责:清洗放左边,转换放右边
原文给了一个很清晰的分层约定:从业务系统到 ODS 做清洗,从 ODS 到 DW 做转换。这个划分不是为了好看,它对应的是一套可回退的设计。
ODS 尽量保持贴近源系统的形态——表结构基本对应、字段基本不改名、只做必要的类型规整和脏数据拦截。这样做的好处是,任何时候怀疑数据有问题,都可以拿 ODS 和源库逐行比对,定位问题出在抽取阶段还是转换阶段。
DW 面向分析,做业务规则计算和聚合。所有口径、维度、指标定义都收敛在这一层。当业务方说「这个指标算错了」,改的是 ODS 到 DW 的转换逻辑,然后从 ODS 重跑,不需要回源库重新抽取——源库那边可能早就把历史数据改得面目全非了。
这套分层的真正价值是后悔药。没有 ODS 这一层,所有清洗转换都在抽取时一次做完,那么一旦发现某条规则写错,唯一的选择是回源重抽,而源数据可能已经变了。有了 ODS 做原始副本,转换层可以随便推翻重来。我在评估一个 ETL 设计好不好时,第一个问题就是「转换逻辑改了,能不能不回源重跑」,答不上来的设计后期一定痛苦。
4. 日志与告警:ETL 的黑匣子怎么设计才敢无人值守
ETL 跑起来之后,绝大部分时间没人在看。任务成功是常态,失败是少数,但恰恰是这少数情况决定了这套流程能不能交给值班同事、能不能半夜不叫人起床。日志和告警就是让 ETL 从「需要盯着」变成「可以信任」的那部分设计。
4.1 三类日志的职责边界:执行过程、错误、总体
原文把日志分成三类,这个分法对应三种完全不同的使用场景,混在一起写会很难查。
执行过程日志是流水账:每一步什么时候开始、什么时候结束、处理了多少行、耗时多久。它的读者是排查性能问题的人。当某个任务从 20 分钟变成 2 小时,翻这张表就能立刻定位到是哪一步变慢了。这类日志量最大,通常只保留最近若干天。
错误日志只在出错时写:出错时间、出错模块、错误码、错误信息、当时的输入参数。它的读者是半夜被叫起来处理故障的人。这张表要求信息密度高,一眼能看出「哪个模块、什么错、影响哪些数据」。
总体日志每次运行一条:批次号、开始时间、结束时间、成功还是失败、整体处理行数。它的读者是看板和趋势分析——这周失败了几次、平均耗时怎么变化的、数据量增长趋势如何。
三类日志分开存的价值在于保留策略可以不同。执行过程日志写得多、留得短;总体日志写得少但留得长,是历史趋势的唯一来源。如果全塞一张表,要么查询慢,要么被迫提前清理掉有用信息。
工具自带的运行日志当然也算日志的一部分,但它通常只记录工具层面的调度信息,业务层面的行数、规则命中情况还得自己埋点。别指望工具日志能回答「这批数据为什么少了两万行」。
4.2 告警的触发条件与降噪
告警设计的第一原则是少而准。每次出错都发邮件的结果是,两周之后所有人都把这类邮件设成自动归档,真正出事时没人看。这是 ETL 运维里最常见的翻车方式之一。
我一般按影响面分三级:
- 阻断级:任务中断、关键表抽取为零行、对账差异超过阈值。立即告警,走即时通讯加短信,值班必须响应。
- 质量级:异常表命中行数环比突增、某条清洗规则命中率异常。汇总成日报,工作时间推送,不打扰夜间。
- 提示级:任务重试后成功、耗时超过基线但未失败。只记录不告警,进周报看趋势。
告警内容要能直接支撑处置,至少包含批次号、模块名、错误摘要、影响行数、建议的重跑命令。只发一句「任务失败」的告警等于没发,收到的人还得从头查起。
4.3 日志表结构与批次号贯穿
三类日志能串起来的关键是一个贯穿全链路的批次号。一批数据从抽取到加载,所有日志、所有异常记录、所有写入的数据行都带同一个batch_id,出问题时才能从结果反查到过程。
-- 总体日志:一次调度运行一条 CREATE TABLE etl_job_log ( batch_id VARCHAR(32) PRIMARY KEY, -- 批次号,格式建议 yyyyMMddHHmmss+序列 job_name VARCHAR(64) NOT NULL, -- 作业名 start_time TIMESTAMP NOT NULL, end_time TIMESTAMP, status VARCHAR(16) NOT NULL, -- RUNNING / SUCCESS / FAILED total_rows BIGINT DEFAULT 0 ); -- 步骤日志:一次运行内每一步一条 CREATE TABLE etl_step_log ( step_id BIGINT PRIMARY KEY, batch_id VARCHAR(32) NOT NULL, -- 关联总体日志 step_name VARCHAR(64) NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP, affect_rows BIGINT DEFAULT 0, -- 本步骤影响行数 status VARCHAR(16) NOT NULL );batch_id用「时间戳 + 序列」的格式,好处是既能按时间排序,又能保证同一秒内多次触发不冲突。affect_rows这个字段看着不起眼,它是排查数据缺口的核心指标——每一步的影响行数连起来看,就能看出数据是在哪一步掉的。比如抽取 10 万行、清洗后 9.8 万行、加载 9.8 万行,那 2000 行是规则拦下的,去异常表按batch_id一查就清楚。
排查时最常用的一个查询是从结果行反查过程:
-- 给定一行异常数据,找出它属于哪一批、经过哪些步骤、被哪条规则拦下 SELECT j.batch_id, j.job_name, j.status, s.step_name, s.affect_rows, r.rule_code, r.reject_type, r.reject_time FROM etl_job_log j JOIN etl_step_log s ON s.batch_id = j.batch_id LEFT JOIN etl_reject_detail r ON r.batch_id = j.batch_id WHERE j.batch_id = :target_batch ORDER BY s.start_time;这条查询把三类日志和异常明细串成一条时间线,是黑匣子的核心用法:任何一行数据出了问题,都能回答「它在哪个批次、经过哪几步、被谁动了」。
5. 避坑与排查:ETL 上线后最常见的几类翻车
原理讲得再顺,落到真实环境里还是会踩坑。下面这几条是从实际项目里攒出来的,每条按「现象 → 原因 → 解决」写,遇到时可以直接对照。
现象一:凌晨跑批从 30 分钟变成 3 小时,没有任何报错。
原因几乎总是执行计划退化。源表数据量增长后统计信息没更新,或者新加的查询条件让索引失效,优化器选了全表扫描。这类问题不报错、不告警,只是慢,最容易被忽略。解决方式是给关键查询建立耗时基线,超过基线的一定比例就发提示级告警,然后定期看执行计划。另一个常见诱因是增量查询的条件字段上没有索引,每次都在做全表扫描再过滤。
现象二:仓库里的行数比源表少几万,但异常表里没有对应记录。
这是最麻烦的一类,因为数据凭空消失且没留痕。常见原因有三个:增量水位线边界用了严格大于导致时间戳完全相同的记录被跳过;抽取查询有隐式的过滤条件(比如关联了一张维表,维表里没有的源记录被内连接掉了);源库在抽取窗口内发生了大批量补录,时间戳落在已经越过的区间。解决方式是每天做一次源表与 ODS 的行数对账,差异超过阈值立即告警;同时把增量上界固定下来,配合回看窗口和主键差集校验。
现象三:任务失败后重跑一次,数据量直接翻倍。
原因是加载环节不幂等,用的是纯插入。第一次跑成功了一半,重跑时把成功的那部分又插了一遍。解决方式是所有写入都改成按主键或按批次先删后插,或者用合并语句做 upsert。这里有个细节:按批次删除要求每行数据都记录了batch_id,否则删不干净;按主键合并则要求主键在目标表上有唯一约束,不然合并语句本身会报错。
现象四:业务方反馈某类客户全都不见了,查下来是被清洗规则拦掉的。
原因是清洗规则写得过宽,把「字段为空」直接等同于「数据无效」,而这类客户的该字段本来就是可选的。解决方式是所有拦截规则上线前先跑 dry-run,只写异常表不拦数据,观察几天确认命中样本;同时每条规则必须有业务方书面确认,规则变更要走版本号,老版本留档。不要相信「这条规则很明显不会误伤」这种判断,误伤几乎总是发生在那句「很明显」上。
现象五:任务失败了三天没人知道,发现时下游报表已经错了三天。
原因是只写了错误日志但没配告警,或者告警发到了一个没人看的邮箱。解决方式是告警分级加责任人绑定,阻断级故障必须走即时通讯并确认签收;同时给总体日志做一个最简单的看板,任何人一眼能看到最近 N 次运行的成功率。日志写得再全,没人看等于没有。
6. 工具与 SQL 的取舍:把复杂度放在哪一层
ETL 的实现方式,原文给了三条路:纯工具、纯 SQL、工具加 SQL 混合。这三种选择背后其实是一个问题——复杂度放在哪一层,谁来承担。
| 维度 | 商业 ETL 工具 | 纯 SQL 实现 | 工具 + SQL 混合 |
|---|---|---|---|
| 上手速度 | 快,组件拖拽即可跑通 | 慢,需要熟悉方言和优化 | 中,骨架用工具搭 |
| 复杂转换能力 | 中等,复杂逻辑要绕 | 强,窗口函数、递归都能写 | 强,重计算下沉到 SQL |
| 调试成本 | 高,黑盒执行,出错难定位 | 低,可单步执行看中间结果 | 中,分层定位 |
| 运行效率 | 受工具引擎限制 | 高,能贴合数据库优化器 | 高 |
| 元数据与血缘 | 自带,可视化 | 基本没有,要自己维护 | 工具侧保留调度血缘 |
| 团队门槛 | 低,业务人员也能改 | 高,需要数据开发 | 中 |
| 变更成本 | 低,改配置 | 高,改代码要回归测试 | 中 |
纯工具的优势是快速建立工程骨架、屏蔽编码、自带调度和日志,代价是灵活性差——遇到需要复杂关联或递归计算的场景,要么绕一大圈,要么被迫写自定义组件。纯 SQL 反过来,灵活、高效、可控,但调度、依赖管理、日志、异常分流全得自己搭,而且代码一多就很难说清血缘关系。
我自己的习惯是把两边各自擅长的事分开:调度编排、连接管理、批次控制、日志落库、异常分流交给工具框架;真正吃算力和逻辑复杂度的转换写成 SQL,作为一个步骤挂进去。这样改口径时只动 SQL,不动流程;改流程时也不碰业务逻辑。这条边界划清楚之后,返工量会明显下降。
判断一个 ETL 设计好不好,我会问三个问题:转换规则改了能不能不回源重跑;任何一行数据出问题能不能追到批次和规则;任务失败重跑一百次结果是不是一样。三个都能答上来,这套设计基本就站得住了。
那份整理成文档的 ETL 设计详解,价值不在于它给了多少结论,而在于它把抽取、清洗转换、加载、日志告警这几块的职责边界讲清楚了,还顺手记下了不少只有踩过才写得出的细节,比如清洗规则要用户确认、异常数据要留档、不要为了跑通就把数据直接过滤掉。这些句子单看很平淡,做过两个项目之后再回头读,才知道每一句背后都是一次对账对不上的深夜。这份文档值得放在手边,遇到设计取舍的时候翻一翻。从那以后我每次开新项目,都会先照着它把源系统清单、清洗规则清单、日志字段这三张表列一遍,再动手写第一行代码——省下来的返工时间远超列表本身。希望帮到你。
本文还有配套的精品资源,点击获取