4. ODS层设计与实现:操作数据存储的概念、同步策略与质量初检
ODS层,全称是Operational Data Store,操作数据存储。很多刚入行的朋友容易把它和STG层搞混。我个人的理解是:STG是临时缓冲区,数据进来转一圈就走;而ODS是源系统数据在数仓里的第一个“家”。它要尽量保持和源系统一致的数据粒度,同时承担数据质量初检的职责。
说白了,ODS层就是数仓和业务系统之间的“翻译官”加“安检员”。它把源系统的数据原样搬过来,顺便做一次基础检查。嗯,这里要注意,ODS不是用来做复杂计算的,它的核心任务是“存”和“传”。
4.1 ODS的核心概念与定位
ODS层在数仓分层架构中处于最底层,直接对接各类源系统。它的设计目标很明确:
- 数据镜像:尽可能保持与源系统一致的数据结构和内容
- 近实时性:支持T+1甚至分钟级的增量同步
- 基础清洗:完成格式校验、空值处理、主键去重等基础工作
- 向下游屏蔽:让DWD、DWS层不再直接面对源系统的复杂性
关键原则:ODS层不做业务逻辑转换,不做聚合计算,不做跨系统关联。这些工作留给DWD层去做。
我在项目中遇到过一种情况:业务团队希望ODS层直接输出报表数据。我坚决反对。ODS一旦承担了业务计算,数据血缘就乱了,出了问题你根本不知道是源系统的问题还是ODS层计算的问题。这个坑,我建议你从一开始就避开。
4.2 ODS与源系统的同步策略
同步策略的选择,直接影响ODS层的数据时效性和系统负载。我一般会从三个维度来评估:数据量级、变更频率、业务容忍度。
| 同步策略 | 适用场景 | 时效性 | 源系统压力 |
|---|---|---|---|
| 全量同步 | 小表、配置表、历史快照表 | T+1 | 较高 |
| 增量同步 | 大表、流水表、日志表 | 分钟级~T+1 | 较低 |
| 实时同步 | 风控、实时大屏、监控 | 秒级 | 高 |
| 拉链表同步 | 缓慢变化维、历史追溯 | T+1 | 中等 |
你想想看,如果一张用户表每天全量同步一次,数据量只有几万条,那没问题。但如果是一张订单流水表,每天几千万条,全量同步就是灾难。我曾经接手过一个项目,ODS层每天全量拉取交易流水,导致ETL窗口从凌晨2点一直跑到早上8点,业务报表根本出不来。后来改成增量同步,窗口压缩到了40分钟。
4.2.1 全量抽取
全量抽取的逻辑很简单:每次同步时,把源表的所有数据全部拉过来,覆盖ODS层的旧数据。
-- 全量抽取示例:每日覆盖用户表
TRUNCATE TABLE ods_user;
INSERT INTO ods_user
SELECT * FROM source_db.user
WHERE create_date <= CURRENT_DATE;
全量抽取的优点是实现简单,数据一致性高。缺点是随着数据量增长,ETL时间会线性增加。我建议全量抽取只用于数据量在百万级以下的表,或者变更频率极低的配置表。
小技巧:对于全量抽取的大表,可以考虑使用分区交换技术。先在新分区中加载数据,然后通过交换分区的方式替换旧数据,这样可以减少对查询的影响。
4.2.2 增量抽取
增量抽取只拉取自上次同步以来发生变化的数据。常见的增量识别方式有三种:
- 时间戳增量:源表有update_time或modify_time字段,根据时间范围过滤
- 日志增量:通过解析数据库的binlog或redo log获取变更
- 标志位增量:源表有is_synced或status字段,同步后更新标志
-- 时间戳增量抽取示例
INSERT INTO ods_order
SELECT * FROM source_db.order
WHERE update_time >= '${last_sync_time}'
AND update_time < '${current_sync_time}';
这里有个坑,我曾经踩过。源系统的update_time字段如果没有索引,增量查询会变成全表扫描,性能还不如全量。所以,做增量抽取前,一定要确认源表有合适的索引。另外,如果源系统存在物理删除操作,时间戳增量是抓不到删除数据的。这时候需要结合日志增量或者软删除标志来处理。
4.3 增量与全量的选择策略
在实际项目中,我一般会按数据表的特性来分类处理:
- 小表(< 100万行):全量抽取,每天一次
- 中表(100万~1000万行):首次全量,后续增量+定期全量校验
- 大表(> 1000万行):增量抽取,分区管理,按需全量
- 日志表:增量抽取,按天分区,保留周期根据业务需求定
注意:增量抽取虽然高效,但存在数据不一致的风险。比如源系统的数据在同步窗口内发生了多次更新,或者存在跨天的事务。我建议每周或每月做一次全量校验,确保增量数据的完整性。
4.4 数据质量初检
ODS层的数据质量初检,是数仓质量保障的第一道防线。我把它分为四个维度:
| 检查维度 | 检查项 | 处理方式 |
|---|---|---|
| 完整性 | 主键是否为空、必填字段是否缺失 | 记录异常日志,标记为脏数据 |
| 准确性 | 数据类型是否匹配、数值范围是否合理 | 转换失败时置为NULL或默认值 |
| 一致性 | 枚举值是否在合法范围内、外键是否存在 | 不合法值统一替换为“未知” |
| 时效性 | 数据时间戳是否在合理范围内 | 超期数据单独分区,不参与下游计算 |
我曾经在ODS层漏掉了日期格式校验,结果下游DWD层在解析日期时频繁报错,排查了整整两天才发现是源系统某个字段混入了“2024-02-30”这种非法日期。从那以后,我在ODS层加了一个通用的日期校验函数,所有日期字段都过一遍。
-- 数据质量初检示例:日期字段校验
CREATE OR REPLACE FUNCTION check_date_valid(date_str STRING)
RETURNS BOOLEAN
AS $$
BEGIN
RETURN TRY_CAST(date_str AS DATE) IS NOT NULL;
END;
$$;
-- 在ETL过程中调用
INSERT INTO ods_user
SELECT
user_id,
user_name,
CASE WHEN check_date_valid(register_date) THEN register_date
ELSE NULL
END AS register_date,
-- 其他字段
FROM source_db.user;
建议:数据质量初检的结果一定要落表。我习惯在ODS层建一张质量检查日志表,记录每次同步的检查结果、异常数据量、处理方式。这样后续排查问题时,有据可查。
4.5 ODS层架构总览
下面这张图展示了ODS层的核心流程和组件关系。你可以看到数据从源系统出发,经过同步策略选择、数据质量初检,最终落地到ODS存储层,并为下游DWD层提供数据服务。
从这张图你可以看到,ODS层不是简单的数据搬运。它需要根据源系统的特点选择合适的同步策略,在数据落地前完成质量初检,最后以规范化的分区表形式存储。只有这样,下游的DWD层才能拿到干净、可靠的数据。
总结一下:ODS层设计的关键在于“存得稳、查得快、质量好”。同步策略要因表制宜,质量初检要防患于未然。记住,ODS层做得扎实,后面的分层工作才能事半功倍。