大数据-235 离线数仓 - 实战:Flume+HDFS+Hive 搭建 ODS/DWD/DWS/ADS 会员分析链路

TL;DR

  • 场景:基于 Flume、HDFS、Hive 验证离线数仓链路,完成会员活跃、新增、留存指标跑通。
  • 结论:该链路适合教学和离线验证,但文中存在命名、路径、日志参数与时间口径不一致的问题。
  • 产出:给出可用于技术博客发布的 SEO 摘要、版本矩阵、错误速查卡,便于读者快速定位和复现。

版本矩阵

已验证 说明
数仓分层链路(ODS→DWD→DWS→ADS) 文章结构完整,分层目标明确,符合典型离线数仓教学路径
Flume Taildir 采集本地日志 配置包含 source/channel/sink,具备基础可运行形态
HDFS 按日志类型+日期分区落盘 /%{logtype}/dt=%{logtime}/ 具备分区设计思路
Hive ODS 外部表挂载 有加载脚本与查询示例,说明链路已打通到 ODS
Hive DWD 明细层清洗 有 DWD 加载脚本与查询结果,具备验证闭环
活跃会员指标链路 提供了 DWS/ADS 脚本顺序,链路定义清楚
新增会员指标链路 提供对应脚本,但正文未展示执行结果
明细会员留存指标链路 提供对应脚本,但正文未展示结果表结构与样例

数据测试

数据采集 => ODS => DWD => DWS => ADS => MySQL 活跃会员、新增会员、会员留存

  • DAU:Daily Active User(日活跃用户)
  • MAU:Monthly Active User(月活跃用户)
  • 假设App的DAU在1000W左右,日启动数据大概1000W条

查看脚本

脚本调用次序:(名字可能有几个不完全能对上,如果名字不对,也不会差太多,修改一下成下面对应的,方便后续的执行)

shell 复制代码
# 加载ODS / DWD 层采集
ods_load_startlog.sh
dwd_load_startlog.sh
# 活跃会员
dws_load_member_start.sh
ads_load_member_active.sh
# 新增会员
dws_load_member_add_day.sh
ads_load_member_add.sh
# 会员留存
dws_load_member_retention_day.sh
ads_load_member_retention.sh

我们之前编写的好的脚本如下所示:

清理数据

将之前测试的数据清理掉,主要是HDFS上数据,避免对后续的操作产生错误的影响:

shell 复制代码
hadoop fs -rm -r /user/data/logs/start/
hadoop fs -rm -r /user/data/logs/enent/

hadoop fs -mkdir /user/data/logs/start
hadoop fs -mkdir /user/data/logs/event

加载数据

我们需要启动Flume,将数据写入,这里准备了几个 log 文件,4MB大小的数据文件。我们将模拟这个文件是程序产生的日志文件,然后Flume采集日志文件后,写入到HDFS中,重新回顾我们的配置文件(flume-log2hdfs2.conf):

shell 复制代码
a1.sources = r1
a1.sinks = k1
a1.channels = c1
# taildir source
a1.sources.r1.type = TAILDIR
a1.sources.r1.positionFile = /opt/wzk/conf/startlog_position.json
a1.sources.r1.filegroups = f1 f2
a1.sources.r1.filegroups.f1 = /opt/wzk/logs/start/.*log
a1.sources.r1.headers.f1.logtype = start
a1.sources.r1.filegroups.f2 = /opt/wzk/logs/event/.*log
a1.sources.r1.headers.f2.logtype = event
# 自定义拦截器
a1.sources.r1.interceptors = i1
a1.sources.r1.interceptors.i1.type = icu.wzk.LogTypeInterceptor$Builder
# memorychannel
a1.channels.c1.type = memory
a1.channels.c1.capacity = 100000
a1.channels.c1.transactionCapacity = 2000
# hdfs sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /user/data/logs/%{logtype}/dt=%{logtime}/
a1.sinks.k1.hdfs.filePrefix = startlog.
a1.sinks.k1.hdfs.fileType = DataStream
# 配置文件滚动方式(文件大小32M)
a1.sinks.k1.hdfs.rollSize = 33554432
a1.sinks.k1.hdfs.rollCount = 0
a1.sinks.k1.hdfs.rollInterval = 0
a1.sinks.k1.hdfs.idleTimeout = 0
a1.sinks.k1.hdfs.minBlockReplicas = 1
# 向hdfs上刷新的event的个数
a1.sinks.k1.hdfs.batchSize = 1000
# Bind the source and sink to the channel
a1.sources.r1.channels = c1
a1.sinks.k1.channel = c1
  • 监听的日志目录是:/opt/wzk/logs/start/.*log,一会儿我们将把文件放到这个目录下
  • 写入的HDFS目录是:/user/data/logs/start 下

我们先启动Flume,然后将对应的日志放入到监听的目录下。

shell 复制代码
flume-ng agent --conf-file /opt/wzk/flume-conf/flume-log2hdfs2.conf -name a1 -Dflume.roog.logger=INFO,console

我的数据如下图所示:

shell 复制代码
2021-09-16 16:55:01.744 [main] INFO  icu.wzk.ecommerce.AppStart - {"app_active":{"name":"app_active","json":{"entry":"2","action":"0","error_code":"0"},"time":1595260800000},"attr":{"area":"洛阳","uid":"2F1009

对应的就截图如下所示: 把这个数据移动到监控目录下:

shell 复制代码
cp /opt/wzk/hive/data/start0721.log /opt/wzk/logs/start/start0721.log

我们观察到flume的程序日志如下:

shell 复制代码
24/08/31 01:59:19 INFO taildir.ReliableTaildirEventReader: headerTable: {}
24/08/31 01:59:19 INFO taildir.ReliableTaildirEventReader: Updating position from position file: /opt/wzk/conf...

对应的截图如下所示: HDFS状态如下:

ODS

定义

ODS 是操作型数据存储层,主要用于存放从业务系统中抽取的原始数据。 数据通常以 业务系统的原始格式 或经过少量标准化处理的形式存储。 是数仓的"数据输入口",负责承接来自业务系统的数据。

功能

数据完整存储:保持数据原始性,便于追溯问题。 解耦:实现业务系统与数据仓库之间的隔离。 实时性支持:如果数据需要实时分析,ODS 可以支持数据的近实时同步。

数据处理

轻度清洗:

  • 去除重复数据。
  • 标准化时间字段、日期字段格式。
  • 增加一些必要的标识字段(如抽取时间、源系统等)。

轻量变换:

  • 数据结构与源系统保持一致,尽量避免复杂的转换。

存储特点

  • 全量存储:ODS 通常存储的是全量数据(包括历史快照)。
  • 分区设计:按时间分区(如按天、小时),以便数据管理和性能优化。
  • 高吞吐:支持高并发写入和抽取。

加载ODS

shell 复制代码
sh ods_load_startlog.sh 2020-07-21

执行结果如小图所示: 我们查看Hive中的数据:

shell 复制代码
hive
-- 执行SQL
use ods;
select * from ods_start_log limit 3;

这里可以看到,外部表已经把数据挂载上了:

加载ODS其他数据

由于数据很多,这里重复上述的日期步骤,把其他数据加载进来,总共的数据如下图所示: 对应的Flume的结果: 对应的HDFS的结果:

DWD

定义

DWD 是明细数据层,存储的是经过清洗和轻度处理的宽表数据。 DWD 数据是细粒度的、面向分析的明细数据,数据通常已经具备一定的 业务逻辑处理 和 质量保证。

功能

数据细化:清洗、标准化后的数据,供后续主题建模使用。 性能优化:减少后续查询时的数据清洗和转换开销。 维度宽表化:对多表进行宽表关联,便于业务分析。

数据处理

数据清洗:

  • 去除脏数据,如无效值、空值处理。
  • 格式统一,如日期格式、金额精度。

数据整合:

  • 按照业务逻辑关联多张表(如订单表和用户表)。
  • 增加计算字段或衍生字段(如消费总金额)。

去重与规范化:

  • 去重是 DWD 层的常见操作,保证数据唯一性。
  • 统一编码、统一时间维度等。

存储特点

  • 宽表存储:尽可能将相关的明细数据组织成宽表,减少关联操作。
  • 分区设计:根据业务需求设计分区,通常也以时间为主要分区键。

加载DWD

shell 复制代码
sh dwd_load_startlog.sh 2020-07-21
# 后续加载了 21-31的数据 重复性高就略过了

执行之后的结果如下图所示: 数据将会写入到:dwd.dwd_start_log 我们查看数据的情况:

shell 复制代码
hive 
-- 查看数据
use dwd;
select * from dwd.dwd_start_log limit 3;

可以看到数据已经加载出来了:

shell 复制代码
1FB872-9A1001   连云港  2F10092A1       1.1.8   common  0.43    PN      chinese iphone

对应的截图如下所示:

加载DWD其他数据

由于数据很多,这里就都加载了,都是重复步骤,就省略了。

错误速查

症状 根因 定位修复
Flume 启动后无预期日志输出 启动参数拼写错误 检查 -Dflume.roog.logger 改为标准 logger 参数,确保 root logger 拼写正确
HDFS 清理后后续采集目录异常 enent 与 event 拼写不一致 对比删除命令与创建命令统一为 event,避免删错目录或遗漏旧数据
Flume 监听不到文件 监听目录与复制目录不一致或文件名不匹配 检查 filegroups 正则与 cp 目标目录确认 /opt/wzk/logs/start/.*log 与实际文件路径、扩展名一致
HDFS 有目录但 Hive 查不到数据 分区目录日期字段依赖拦截器,未正确写入 logtime 检查自定义拦截器 LogTypeInterceptor 输出 header 确保拦截器正确注入 logtype/logtime,并与 Hive 分区字段一致
ODS 可查但 DWD 无数据 DWD 脚本日期参数与 ODS 实际分区不匹配 核对 sh dwd_load_startlog.sh 2020-07-21 与 ODS 分区统一业务日期口径,确认源分区存在后再跑 DWD
日志文件复制后无新增消费 Taildir positionFile 记录了旧偏移 检查 /opt/wzk/conf/startlog_position.json 测试前清理或重置 positionFile,避免 Flume 认为已消费
HDFS 文件滚动异常或文件长期不关闭 rollCount/rollInterval/idleTimeout 均设为 0 检查 sink 滚动参数按测试场景设置合理滚动阈值,避免小样本测试不落稳态文件
文章中的日期看起来混乱 样例日志时间、执行日期、日志截图时间跨 2020/2021/2024 核对正文中的日期片段发布前统一演示日期,避免读者误判数据链路有问题
脚本执行顺序理解成本高 只给脚本名,未明确"依赖关系/输入输出表" 检查"查看脚本"章节增加每个脚本对应的输入层、输出层、目标表说明
读者能跑通 ODS/DWD,但无法验证 ADS 指标正确性 缺少 ADS 结果查询示例 检查 DWS/ADS 段落内容补充至少一条活跃/新增/留存的 SQL 查询结果示例

其他系列

🚀 AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究 ,持续打造实用AI工具指南! AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地

💻 Java篇持续更新中(长期更新)

Java-218 RocketMQ Java API 实战:同步/异步 Producer 与 Pull/Push Consumer MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS已完结,GuavaCache已完结,EVCache已完结,RabbitMQ已完结,RocketMQ正在更新... 深入浅出助你打牢基础!

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈! 大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解

相关推荐
Moment2 小时前
一周重写 Next.js?Cloudflare 和 AI 做到了😍😍😍
前端·javascript·后端
CodeSheep2 小时前
同事去年绩效是C,提离职领导死活不让走,后来领导私下说他走了,就没人背这个绩效了
前端·后端·程序员
怕浪猫2 小时前
第19章:Go语言工具链与工程实践
后端·go·编程语言
BingoGo3 小时前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·laravel
风象南3 小时前
WHAT? AI把我仓库内容全删了!!!
后端
摸鱼的春哥3 小时前
春哥的Agent通关秘籍12:本地RAG实战(中下)向量化与落库
前端·javascript·后端
JaguarJack3 小时前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
摸鱼的春哥3 小时前
专家实验让AI做战争决策,AI的选择太暴力了
前端·javascript·后端
Victor35610 小时前
MongoDB(15) 如何在MongoDB中启用身份验证?
后端