场景:构建离线数据仓库的 ODS(Operational Data Store)层,使用 Hive 外部表承接原始日志数据,按天分区管理,并对 JSON 格式字段进行高效解析与展开。
结论:ODS 层应坚持“贴源、分区、外部表”原则;JSON 字段优先使用json_tuple批量提取,数组结构通过explode + lateral view展开。
产出:提供可复用的建表模板、分区管理 SQL、按天装载脚本骨架,以及 JSON 单值/数组/展开查询的标准范式。
一、ODS 层定义与核心特性
1.1 什么是 ODS?
ODS(Operational Data Store)是数据仓库体系中的贴源层,用于存储未经深度加工的原始业务数据。它不承担复杂计算或历史分析任务,而是作为后续 DWD/DWS 层的数据入口。
1.2 ODS 的关键特性
| 特性 | 说明 |
|---|---|
| 贴源性 | 数据结构、内容与源系统高度一致,不做清洗或聚合 |
| 分区管理 | 按时间(如天)分区,便于增量处理和生命周期管理 |
| 外部表 | 使用 EXTERNAL TABLE,避免误删 HDFS 原始数据 |
| 非历史性(相对) | 虽保留历史分区,但通常只存近1-2年,不长期归档 |
| 面向批处理 | 支持 T+1 离线 ETL,非实时流 |
✅ 最佳实践:ODS 不等于“脏数据池”,仍需保证格式规范(如每行一个完整 JSON)。
二、ODS 层建表示例(Hive 外部表 + 按天分区)
-- 创建 ODS 库(若未存在)
CREATE DATABASE IF NOT EXISTS ods;
USE ods;
-- 创建外部表:原始日志以字符串形式存储
CREATE EXTERNAL TABLE ods.ods_start_log (
`str` STRING COMMENT '原始日志 JSON 字符串'
)
COMMENT '用户启动日志 ODS 表'
PARTITIONED BY (`dt` STRING COMMENT '日期分区,格式:yyyy-MM-dd')
STORED AS TEXTFILE
LOCATION '/user/data/logs/start';
🔍 关键点:
EXTERNAL:删除表时仅删元数据,HDFS 文件保留;PARTITIONED BY (dt):支持按天增量处理;LOCATION:指向 HDFS 日志目录,需提前创建并授权。
三、分区管理:增删查
-- 添加分区(需确保 HDFS 对应目录已有数据)
ALTER TABLE ods.ods_start_log ADD PARTITION (dt='2025-08-02');
-- 删除分区(仅删元数据,HDFS 文件仍保留)
ALTER TABLE ods.ods_start_log DROP PARTITION (dt='2025-08-02');
-- 查看所有分区
SHOW PARTITIONS ods.ods_start_log;
⚠️ 常见错误:
ADD PARTITION后查不到数据?
原因:HDFS 分区目录为空或路径不匹配。
解决:先确认/user/data/logs/start/dt=2025-08-02/下有文件。
四、按天数据装载脚本骨架(Shell)
#!/bin/bash
# 文件:ods_load_startlog.sh
# 功能:自动为昨日日志添加 Hive 分区
APP=ods
source /etc/profile
# 参数处理:支持手动传入日期,否则默认昨天
if [ -n "$1" ]; then
do_date=$1
else
do_date=$(date -d "-1 day" +%F)
fi
echo "Adding partition for date: $do_date"
# 执行 Hive SQL
hive -e "
ALTER TABLE ${APP}.ods_start_log ADD PARTITION (dt='${do_date}');
"
✅ 运行方式:
chmod +x ods_load_startlog.sh ./ods_load_startlog.sh # 处理昨天 ./ods_load_startlog.sh 2025-08-02 # 指定日期🔧 权限问题:确保 Hive 用户对 HDFS 目录有读权限,必要时执行:
hdfs dfs -mkdir -p /user/data/logs/start hdfs dfs -chown hive:hadoop /user/data/logs/start
五、JSON 数据解析范式
假设原始日志每行为合法 JSON,例如:
{"uid":"u1001","event":"start","ts":1717020800,"props":{"os":"Android","version":"3.2.1","ids":[101,102,103]}}
5.1 单值字段提取 → get_json_object(简单场景)
SELECT
get_json_object(str, '$.uid') AS uid,
get_json_object(str, '$.event') AS event,
get_json_object(str, '$.props.os') AS os
FROM ods.ods_start_log
WHERE dt = '2025-08-02';
❌ 缺点:每次只能取一个字段,效率低。
5.2 批量提取顶层字段 → json_tuple(推荐!)
SELECT
t.uid,
t.event,
t.ts,
t.props
FROM ods.ods_start_log
LATERAL VIEW json_tuple(str, 'uid', 'event', 'ts', 'props') t
AS uid, event, ts, props
WHERE dt = '2025-08-02';
✅ 优势:一次调用解析多个字段,性能优于多次
get_json_object。
5.3 数组展开 → explode + lateral view
目标:将 props.ids 数组 [101,102,103] 拆成多行。
WITH parsed AS (
SELECT
uid,
event,
ts,
get_json_object(props, '$.ids') AS ids_str -- 得到 "[101,102,103]"
FROM (
SELECT
t.uid,
t.event,
t.ts,
t.props
FROM ods.ods_start_log
LATERAL VIEW json_tuple(str, 'uid', 'event', 'ts', 'props') t
AS uid, event, ts, props
) tmp
WHERE dt = '2025-08-02'
)
SELECT
uid,
event,
ts,
id_item
FROM parsed
LATERAL VIEW explode(
split(regexp_replace(ids_str, '\$$|\$$', ''), ',')
) t2 AS id_item
WHERE id_item != ''; -- 过滤空值
🔑 关键步骤:
regexp_replace(ids_str, '\$$|\$$', '')→ 去掉[和]split(..., ',')→ 转为 Arrayexplode+lateral view→ 展开为多行⚠️ 注意:若 JSON 中
ids本身是空数组[],需额外处理避免空字符串。
六、错误速查表
| 症状 | 根因 | 修复方案 |
|---|---|---|
| 分区添加后查不到数据 | HDFS 分区目录无文件 | 先将日志文件放入 /.../dt=2025-08-02/ |
| 脚本报 “bad interpreter” | Shebang 写成全角 #! |
改为半角 #!/bin/bash |
APP=ODS 找不到表 |
Hive 库名大小写敏感 | 统一用小写:APP=ods |
| JSON 解析结果为 NULL | JSON 格式非法或路径错误 | 抽样检查:SELECT str FROM ... LIMIT 5 |
explode 不生效 |
输入是字符串而非 Array | 必须先 regexp_replace + split 转 Array |
get_json_obejct 报错 |
函数名拼写错误 | 正确为 get_json_object |
七、总结:ODS 层建设 Checklist
✅ 使用 外部表(EXTERNAL TABLE)
✅ 按 天分区(dt string)
✅ LOCATION 指向 HDFS 日志目录
✅ 原始数据保持 一行一 JSON
✅ JSON 解析优先 json_tuple
✅ 数组展开用 explode + lateral view
✅ 装载脚本支持 参数化日期 + 幂等性
📌 记住:ODS 是数仓的“第一道防线”——不求快,但求稳、准、全。
共同学习,写下你的评论
评论加载中...
作者其他优质文章