十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

数仓分层实战:ODS、CDM、ADS三层架构设计与建表规范

数仓分层实战:ODS、CDM、ADS三层架构设计与建表规范 1. 数仓分层的本质不是技术炫技是工程管理刚入行那会儿我对数仓分层的理解特别朴素——不就是把表按前缀分成几层嘛ods_、dwd_、dws_、ads_建表的时候选个前缀就完事了。后来参与了一个从零搭建的数仓项目数据量不大但业务方的需求一天变三次我才真正意识到分层不是技术问题是工程管理问题。你想想如果没有分层所有数据加工逻辑全部堆在一起会发生什么一个业务口径的调整可能要改十几张表上游源系统字段一变更下游所有报表全部报错新人接手项目打开几百张表完全不知道从哪看起。这些问题的根源都是因为数据加工链路没有清晰的分层边界。数仓分层的核心价值说白了就三件事职责分离、复用提效、问题隔离。ODS层负责对接源系统做最原始的数据落地CDM层有些团队叫DWDDWS负责清洗、整合、轻度汇总ADS层负责面向具体应用场景做最终输出。每一层只做自己该做的事上游变了只影响相邻层不会一崩全崩。这篇文章我会从实际项目出发把数仓分层的设计思路、每层的具体职责、建表规范、常见坑点全部拆开讲。不管你是刚接触数仓的新人还是正在重构老数仓的老手应该都能找到可以直接抄作业的东西。2. 分层架构设计为什么是ODS、CDM、ADS这三层2.1 分层方案选型的底层逻辑业界常见的分层方案有好几种三层、四层、五层都有。我见过最夸张的一个项目分了七层结果维护成本高到没人愿意碰。也见过只有两层的ODS直接到ADS短期跑得挺快半年后表数量爆炸根本维护不动。三层架构ODS → CDM → ADS是我个人最推荐的方案原因很简单它在灵活性和维护成本之间找到了一个比较好的平衡点。ODS保持和源系统同构CDM做整合和汇总ADS面向应用。每一层的边界清晰职责不重叠。有人会问CDM层要不要再拆成DWD和DWS我的建议是看团队规模和数据复杂度。如果团队只有两三个人数据域不超过五个CDM一层就够了内部用命名规范区分明细和汇总即可。如果团队十几个人数据域几十个那拆成DWD明细和DWS汇总会更好管理因为不同角色的开发人员关注的层不一样。还有一种常见的四层方案是ODS → DWD → DWS → ADS其实本质上是把CDM拆开了。选哪种不重要重要的是团队内部要统一认知不能同一个人今天写DWD明天写CDM那就乱了。2.2 各层职责的详细拆解先看一张各层职责的对照表后面再逐层展开层次核心职责数据特征保留周期主要使用者ODS源系统数据落地不做业务逻辑加工与源系统同构含全量/增量通常30-90天数仓开发CDM-DWD数据清洗、标准化、维度关联明细粒度宽表为主通常1-3年数仓开发、分析师CDM-DWS轻度汇总按主题域聚合汇总粒度指标预计算通常1-3年分析师、数据产品ADS面向应用场景的最终输出高度聚合直接可用按需通常1年业务方、BI报表ODS层的核心原则是不改数据。源系统是什么样ODS就什么样。字段名不改、类型不改、不做清洗。唯一要做的加几个管理字段etl_time抽取时间、dt分区日期、src_system来源系统标识。这样做的好处是当CDM层加工逻辑出问题时可以随时回到ODS重新跑数不用担心源头数据被污染。我见过有团队在ODS层就做清洗和转换的结果后来发现清洗逻辑有问题想重新跑数发现原始数据已经找不回来了。这个坑一定要避开。CDM层是数仓的核心价值所在。DWD做明细加工把不同源系统的数据按照统一标准整合到一起——比如用户ID的格式统一、时间字段的时区统一、枚举值的编码统一。DWS做汇总把常用的指标提前算好比如按天、按品类、按地区的GMV汇总。这一层的设计质量直接决定了数仓好不好用。ADS层是面向业务的。每个报表、每个API接口、每个数据产品在ADS层都应该有对应的表。ADS层的表可以冗余、可以反范式、可以为了查询性能做各种优化因为它就是最终消费层不需要考虑复用性。2.3 分层边界的划定技巧分层最容易出问题的地方是边界模糊。什么算DWD该做的什么算DWS该做的我的经验是看两个维度粒度和复用范围。粒度维度很好理解DWD保持明细粒度一行数据对应一个业务事件或一个实体状态DWS做聚合一行数据对应一组维度组合。如果一个加工逻辑改变了数据粒度那它就应该从DWD上升到DWS。复用范围维度更关键如果一个指标只有某一个报表用那放在ADS层算就行了如果三个以上的报表都要用同一个指标那就应该下沉到DWS层预计算。这个判断标准可以帮你避免两个极端——既不会把所有计算都堆在ADS层导致重复开发也不会把一次性计算放到DWS层浪费存储和计算资源。实操心得我通常会在项目初期先快速搭建ODS和ADS两层中间用CDM层做过渡。等业务稳定了再逐步把ADS层中复用度高的逻辑下沉到CDM层。这种先跑通再优化的方式比一开始就追求完美分层要务实得多。3. 各层建表规范与核心细节3.1 ODS层建表同步策略比表结构更重要ODS层的表结构其实没什么好设计的跟着源系统走就行。真正需要花心思的是同步策略——全量同步还是增量同步每天一次还是每小时一次这些问题直接影响到后续所有层的时效性和数据准确性。全量同步适合数据量小、变化频繁的维度表比如商品分类表、地区编码表。每天凌晨把源表全量拉过来覆盖简单粗暴但有效。增量同步适合数据量大的事实表比如订单表、日志表只拉当天新增或变更的数据。增量同步有个坑一定要注意源系统如果没有updated_at字段增量拉取会漏数据。物理删除的记录你拉不到更新但没改时间戳的记录你也拉不到。遇到这种情况要么推动源系统加字段要么退而求其次用全量对比的方式做增量识别——每天拉全量和昨天的数据做diff找出新增和变更的记录。这种方式对源系统压力大但至少数据不会丢。分区设计上ODS层通常按天分区分区字段用dt日期字符串格式yyyy-MM-dd。如果源系统数据量特别大可以考虑按小时分区。分区字段一定要放在所有字段的最后这是Hive表的规范虽然新版本不强制但养成习惯没坏处。-- ODS层订单表建表示例 CREATE TABLE IF NOT EXISTS ods_order_info ( order_id STRING COMMENT 订单ID, user_id STRING COMMENT 用户ID, product_id STRING COMMENT 商品ID, order_amount DECIMAL(18,2) COMMENT 订单金额, order_status STRING COMMENT 订单状态, create_time STRING COMMENT 创建时间, update_time STRING COMMENT 更新时间, etl_time STRING COMMENT ETL抽取时间, src_system STRING COMMENT 来源系统 ) COMMENT ODS-订单信息表 PARTITIONED BY (dt STRING COMMENT 分区日期) STORED AS ORC;3.2 CDM层设计维度建模的落地要点CDM层是整个数仓的灵魂。这一层做得好不好直接决定了数仓能不能支撑起复杂的分析需求。DWD层的核心工作是清洗和整合。清洗包括去空值、去重复、格式统一、异常值处理。整合包括多源合并、维度关联、编码映射。举个例子用户信息可能来自APP端和Web端两个系统用户ID格式不一样注册时间时区不一样性别字段一个用0/1一个用M/F。DWD层就要把这些统一成一套标准。维度关联是DWD层最耗资源的部分。一个订单宽表可能要关联用户维度、商品维度、地区维度、渠道维度等七八张维表。关联方式用LEFT JOIN还是INNER JOIN关联字段有没有空值维表有没有重复数据——每一个细节都会影响最终结果。注意维表关联前一定要做数据质量检查。我踩过最惨的坑是维表里有重复的维度ID导致订单宽表关联后数据量翻倍下游所有指标全部虚高。后来养成了习惯每次关联前先跑一遍维表主键唯一性校验。DWS层的设计要围绕主题域来组织。常见的主题域包括交易域、用户域、流量域、商品域、营销域。每个主题域下根据分析需求设计汇总表。比如交易域可以设计每日品类销售汇总表、每日地区销售汇总表、每日渠道转化汇总表等。DWS层的汇总粒度选择有个原则宁可细一点不要粗过头。因为从细粒度汇总到粗粒度很容易从粗粒度拆回细粒度就不可能了。比如你按天品类地区汇总了后来业务要看按小时的那就得回到DWD重新算。所以如果资源允许DWS层尽量保留较细的粒度。3.3 ADS层输出面向场景的极致优化ADS层不需要考虑复用性只需要考虑查询性能和业务易用性。这一层的表可以大量冗余字段可以提前算好各种比率和排名可以用最宽的宽表来减少JOIN操作。ADS层的表命名建议带上业务场景标识比如ads_sales_daily_report销售日报、ads_user_growth_analysis用户增长分析。这样业务方一看表名就知道是哪张表不用去翻文档。数据更新策略上ADS层通常采用全量覆盖或分区覆盖。全量覆盖适合数据量小的报表表每天重跑一次全量。分区覆盖适合数据量大的表只更新当天的分区。如果报表需要实时性ADS层还可以对接实时计算链路用Flink或Spark Streaming做增量更新。ADS层还有一个容易忽略的点数据权限。因为ADS层直接面向业务方不同业务线只能看自己领域的数据。建表的时候就要考虑好权限隔离方案是按库隔离、按表隔离还是按行隔离这个要在设计阶段就定好不然后期改起来很痛苦。4. 完整实操流程从零搭建一个分层数仓4.1 环境准备与工具选型搭建数仓之前先把工具链定下来。这块没有标准答案根据团队技术栈和预算来选就行。存储和计算引擎方面HiveSpark是经典组合适合离线批处理场景社区成熟资料多。如果预算充足且追求性能可以考虑用云上的数据仓库服务省去运维成本。如果数据量不大Doris或ClickHouse也能胜任查询性能比Hive好很多。调度工具方面Airflow是开源方案里比较主流的选择DAG编排灵活社区活跃。如果团队已经在用云平台云厂商自带的调度服务也能用省去部署维护的麻烦。DolphinScheduler在国内团队中用得也比较多界面友好上手快。开发规范方面建议统一SQL编码风格比如关键字大写、字段对齐、注释完整。可以配一个SQL检查工具在代码提交前自动检查规范。这个投入不大但长期收益很高。4.2 ODS层数据接入实操假设我们要接入一个MySQL的订单库具体步骤是这样的第一步在Hive中建ODS表表结构和源表保持一致加上etl_time、dt、src_system三个管理字段。第二步配置数据同步任务。如果用Sqoop可以写一个增量同步脚本按update_time字段做增量拉取。如果用DataX配置json文件定义reader和writer。如果用Flink CDC可以直接订阅binlog做实时同步。# Sqoop增量同步示例 sqoop import \ --connect jdbc:mysql://source-host:3306/order_db \ --username reader \ --password ****** \ --table order_info \ --target-dir /warehouse/ods/order_info/dt$(date %Y-%m-%d) \ --incremental lastmodified \ --check-column update_time \ --last-value 2024-01-01 00:00:00 \ --fields-terminated-by \001 \ --null-string \\N \ --null-non-string \\N第三步配置调度。每天凌晨源库压力小的时候跑同步任务同步完成后触发下游CDM层的加工任务。实操心得ODS层同步任务一定要加数据量监控。如果某天同步的数据量突然比平时少了一半很可能是源系统出问题了或者同步任务部分失败了。我一般会配一个简单的校验规则当天同步条数不能低于过去7天平均值的80%低于就告警。4.3 CDM层加工逻辑实现DWD层的加工SQL通常比较长因为要关联多张维表。写这类SQL有几个技巧先做过滤再做关联。把不需要的数据尽早过滤掉减少关联时的数据量。比如只处理当天的增量数据而不是全量数据。维表关联时注意区分事实表和维表的粒度。如果维表有缓慢变化维SCD要确定用哪个时间点的维度版本。通常用订单创建时间对应的维度版本而不是当前最新版本。-- DWD层订单宽表加工示例 INSERT OVERWRITE TABLE dwd_order_detail PARTITION(dt${bizdate}) SELECT o.order_id, o.user_id, u.user_name, u.user_level, o.product_id, p.product_name, p.category_id, p.category_name, o.order_amount, o.order_status, o.create_time, o.update_time FROM ods_order_info o LEFT JOIN dim_user u ON o.user_id u.user_id AND u.dt ${bizdate} LEFT JOIN dim_product p ON o.product_id p.product_id AND p.dt ${bizdate} WHERE o.dt ${bizdate} AND o.order_status ! DELETED;DWS层的加工逻辑相对简单主要是GROUP BY聚合。但要注意聚合维度的选择和指标的预计算方式。比如GMV要不要含退款下单用户数要不要去重这些口径问题一定要和业务方确认清楚写进数据字典里。4.4 ADS层报表输出与验证ADS层的加工逻辑最灵活完全根据报表需求来。比如销售日报需要展示每日GMV、订单量、客单价、同比环比等指标那就在ADS层建一张宽表把这些指标全部算好。数据验证是ADS层最重要的环节。我通常做三层验证第一层是总量验证ADS层的汇总数据要和ODS层的原始数据对得上第二层是逻辑验证抽样几条数据手工核对计算过程第三层是业务验证让业务方看报表数据是否符合预期。5. 常见问题与排查技巧实录5.1 数据质量问题的排查思路数仓开发最怕的就是数据质量问题。我整理了一个常见问题速查表问题现象可能原因排查方法解决方案数据量突然翻倍维表关联重复检查维表主键唯一性维表去重或改用LEFT JOIN数据量突然减半上游同步失败检查ODS层数据量重跑同步任务指标数值异常空值处理不当检查JOIN字段空值率空值默认值处理分区数据缺失调度任务失败检查调度日志补跑缺失分区数据延迟上游产出晚检查依赖任务完成时间调整调度依赖和告警排查数据问题有个基本原则从上游往下游查。先确认ODS层数据没问题再看DWD层最后看DWS和ADS层。这样能快速定位问题出在哪一层。5.2 分层设计中的典型坑点第一个坑是过度分层。有些团队为了追求架构优雅把分层搞得太细结果每层之间的依赖关系复杂到没人能理清楚。我的建议是分层数量控制在三到四层每层的职责用一句话能说清楚。第二个坑是跨层引用。ADS层直接查ODS层的数据跳过了CDM层。这种做法短期看省事了长期看是灾难——因为ODS层的数据没有经过清洗和整合直接用在报表里很容易出问题。而且一旦ODS层表结构变更ADS层也要跟着改维护成本翻倍。第三个坑是分层不一致。同一个指标在DWS层和ADS层算出来的结果不一样因为两层的计算逻辑没有对齐。这种问题最隐蔽因为单看每一层的数据都对但放在一起就矛盾了。解决办法是建立指标字典所有指标的计算口径统一管理各层引用同一套定义。实操心得我习惯在项目初期就建一个数据字典文档记录每个指标的的业务口径、计算逻辑、所在层级、负责人。这个文档看起来不起眼但在排查问题和交接工作的时候能救命。5.3 性能优化的实战技巧数仓跑得慢是常态但有些慢是可以避免的。小文件问题是最常见的性能杀手。ODS层频繁增量同步会产生大量小文件Hive查询时每个小文件都要启动一个Map任务光调度开销就拖垮了性能。解决办法是定期做小文件合并或者在建表时设置合适的文件大小参数。数据倾斜是另一个大问题。某个key的数据量特别大导致一个Reduce任务跑几个小时。常见的解决方案包括加盐打散、Map Join、调整并行度。具体用哪种要看场景加盐适合GROUP BY场景Map Join适合大表关联小表。分区裁剪是最容易忽略的优化点。写SQL的时候一定要带上分区过滤条件不然全表扫描再好的硬件也扛不住。我见过有开发写SQL不带分区条件跑一次查询扫了几百GB数据被DBA追着骂。6. 分层架构的演进与扩展思路数仓分层不是一成不变的。业务在发展数据量在增长分层架构也要跟着调整。初期阶段数据量小、业务简单ODSADS两层就够了。快速上线快速验证不用过度设计。成长阶段业务复杂度上来了指标复用需求多了这时候引入CDM层做整合和汇总。把ADS层中重复的计算逻辑下沉到CDM层减少重复开发。成熟阶段数据域多了团队大了可以考虑把CDM拆成DWD和DWS甚至引入DIM层专门管理维度。同时建立数据治理体系包括元数据管理、数据质量监控、数据血缘追踪等。实时化是另一个演进方向。传统的离线数仓T1的时效性越来越不能满足业务需求。可以在现有分层架构的基础上增加实时链路ODS层用CDC做实时接入CDM层用Flink做实时加工ADS层对接实时报表。离线链路和实时链路并行互为补充。最后分享一个我在实际项目中的体会分层架构的价值不在于分了几层而在于每一层的边界是否清晰、职责是否明确。我见过三层架构跑得很好的团队也见过五层架构一团糟的项目。关键还是要把每一层的输入输出定义清楚把数据流向和依赖关系管理好。工具和架构都是手段最终目的是让数据能够高效、准确地支撑业务决策。
返回列表