TL;DR
- 场景:MySQL 数据用 DataX 落到 HDFS,再按 dt 分区加载到 Hive ODS 外部表
- 结论:ODS 外部表 + HDFS 分区目录 + ALTER TABLE ADD PARTITION 是最低可用闭环
- 产出:7 张 ODS 表建表模板 + 一键日分区落盘/迁移/挂载脚本(可复用)


基本介绍
- ODS层表结构域源数据基本类似(列表、数据类型)
- ODS层表名遵循统一的规范
在大数据体系中,ODS(Operational Data Store),即操作数据存储,是数据仓库中的重要组成部分,起着承上启下的作用。ODS主要是用于存储原始的、经过轻度处理的数据,通常直接从业务系统(如ERP、CRM等)中抽取而来。
ODS是大数据架构中的数据层之一,它是指在数据从业务系统到数据仓库的过程中,用于临时存放和管理数据的区域。ODS一般用来存储接近实时的、较为原始的操作型数据,为上层的数据清洗、加工、分析提供基础。
功能定位
- 作为数据仓库的前置层。
- 承接从业务系统或外部来源抽取的数据。
- 数据通常不进行深度加工和聚合,保持业务的原貌。
数据特点
- 原始性:数据通常是经过轻度标准化后的原始数据,保留业务系统的字段格式。
- 实时性:相比数据仓库,ODS的数据更新更为及时,可接近实时。
- 短期存储:ODS中的数据存储周期通常较短,一般只保存最近一段时间的数据(如7天、30天)。
ODS的作用
缓冲层作用
- ODS是数据进入数据仓库的缓冲区,避免直接从业务系统中抽取数据对业务系统造成压力。
- 将业务系统与数据仓库解耦,降低系统之间的耦合性。
数据整合和清洗
- 对原始数据进行轻度的清洗(如去重、格式转换、简单校验等)。
- 为不同来源的数据提供统一的格式和标准,便于后续分析和处理。
支持实时查询
- 提供较实时的数据查询服务,支持业务运营和分析需求。
- 适合处理短期内的数据报表、运营监控等需求。
数据追溯
保留业务数据的原始状态,方便数据问题的追溯和修复。
ODS层建表
所有的表都是分区表,字段之间的分隔符号都是",",为表的数据文件指定的位置。
ods_trade_orders
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_orders`;
CREATE EXTERNAL TABLE `ods.ods_trade_orders`(
`orderid` int,
`orderno` string,
`userid` bigint,
`status` tinyint,
`productmoney` decimal(10, 0),
`totalmoney` decimal(10, 0),
`paymethod` tinyint,
`ispay` tinyint,
`areaid` int,
`tradesrc` tinyint,
`tradetype` int,
`isrefund` tinyint,
`dataflag` tinyint,
`createtime` string,
`paytime` string,
`modifiedtime` string)
COMMENT '订单表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/orders/';
加载结果如下所示: 
ods_trade_order_product
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_order_product`;
CREATE EXTERNAL TABLE `ods.ods_trade_order_product`(
`id` string,
`orderid` decimal(10,2),
`productid` string,
`productnum` string,
`productprice` string,
`money` string,
`extra` string,
`createtime` string)
COMMENT '订单明细表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/order_product/';
执行结果如下图所示: 
ods_trade_product_info
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_product_info`;
CREATE EXTERNAL TABLE `ods.ods_trade_product_info`(
`productid` bigint,
`productname` string,
`shopid` string,
`price` decimal(10,0),
`issale` tinyint,
`status` tinyint,
`categoryid` string,
`createtime` string,
`modifytime` string)
COMMENT '产品信息表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/product_info/';
对应的结果如下图所示: 
ods_trade_product_category
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_product_category`;
CREATE EXTERNAL TABLE `ods.ods_trade_product_category`(
`catid` int,
`parentid` int,
`catname` string,
`isshow` tinyint,
`sortnum` int,
`isdel` tinyint,
`createtime` string,
`level` tinyint)
COMMENT '产品分类表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/product_category';
对应的结果如下图所示: 
ods_trade_shop
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_shops`;
CREATE EXTERNAL TABLE `ods.ods_trade_shops`(
`shopid` int,
`userid` int,
`areaid` int,
`shopname` string,
`shoplevel` tinyint,
`status` tinyint,
`createtime` string,
`modifytime` string)
COMMENT '商家店铺表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/shops';
执行结果如下图所示: 
ods_trade_shop_admin_org
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_shop_admin_org`;
CREATE EXTERNAL TABLE `ods.ods_trade_shop_admin_org`(
`id` int,
`parentid` int,
`orgname` string,
`orglevel` tinyint,
`isdelete` tinyint,
`createtime` string,
`updatetime` string,
`isshow` tinyint,
`orgType` tinyint)
COMMENT '商家地域组织表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/shop_org/';
执行结果如下图所示: 
ods_trade_payments
sql
use ods;
DROP TABLE IF EXISTS `ods.ods_trade_payments`;
CREATE EXTERNAL TABLE `ods.ods_trade_payments`(
`id` string,
`paymethod` string,
`payname` string,
`description` string,
`payorder` int,
`online` tinyint)
COMMENT '支付方式表'
PARTITIONED BY (`dt` string)
row format delimited fields terminated by ','
location '/user/data/trade.db/payments/';
执行结果如下图所示: 
ODS层数据加载
编写脚本
DataX仅仅是将数据从MySQL导入到了HDFS,数据并没有与Hive表建立起关联。现在我们要编写脚本,任务就是:数据迁移、数据加载到ODS层 编写脚本:
shell
vim /opt/wzk/hive/ods_load_trade.sh
写入的内容如下所示:
shell
#!/bin/bash
source /etc/profile
if [ -n "$1" ] ;then
do_date=$1
else
do_date=`date -d "-1 day" +%F`
fi
# 创建目录
hdfs dfs -mkdir -p /user/data/trade.db/product_category/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/shops/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/shop_org/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/payments/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/orders/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/order_product/dt=$do_date
hdfs dfs -mkdir -p /user/data/trade.db/product_info/dt=$do_date
# 数据迁移
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/product_category.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/shops.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/shop_org.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/payments.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/orders.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/order_product.json
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/product_info.json
# 加载 ODS 层数据
sql="
alter table ods.ods_trade_orders add partition(dt='$do_date');
alter table ods.ods_trade_order_product add
partition(dt='$do_date');
alter table ods.ods_trade_product_info add
partition(dt='$do_date');
alter table ods.ods_trade_product_category add
partition(dt='$do_date');
alter table ods.ods_trade_shops add partition(dt='$do_date');
alter table ods.ods_trade_shop_admin_org add
partition(dt='$do_date');
alter table ods.ods_trade_payments add
partition(dt='$do_date');
"
hive -e "$sql"
编写的结果如下所示: 
运行测试
shell
sh /opt/wzk/hive/ods_load_trade.sh 2020-07-12
执行结果如下所示: 
错误速查
| 症状 | 根因 | 定位 | 修复 |
|---|---|---|---|
| Hive 表能建成功但 select 查不到数据 | 只把数据写到 HDFS 了,Hive 分区元数据没加 | show partitions ods.xxx; 为空;HDFS 目录已存在 dt=... |
执行 alter table ... add partition(dt='...')(你已在脚本里做了) |
| add partition 成功但仍查不到 | 分区目录与 table location 不匹配(路径/表名写错、少/多了子目录) | desc formatted ods.xxx; 看 Location;hdfs dfs -ls 看实际目录 |
修正建表 location 或修正脚本创建目录的路径,保证 location/dt=日期/ 一致 |
| add partition 报 "Partition already exists" | 重跑同一天任务未做幂等 | Hive 执行日志 | 改为 add if not exists partition(...)(不同 Hive 版本语法可能不同)或重跑前 drop partition |
| DataX 运行成功但 Hive 报字段解析异常/列错位 | 源数据分隔符/转义与 Hive fields terminated by ',' 不一致;字段包含逗号/引号 |
抽样 hdfs dfs -cat ...head 检查原始行;看错位列 |
调整 Hive 表分隔符定义或清洗源数据 |
金额字段 decimal(10,0) 读出来为 NULL/截断 |
源数据有小数,但表定义为 0 小数位;或源是字符串不规范 | 抽样原始数据对比 Hive schema | 对齐精度:如 decimal(10,2);或先按 string 落 ODS、后续 DWD 再强转 |
hdfs dfs -mkdir 报无权限 |
HDFS 权限/用户不对 | hdfs dfs -ls /user/data、看报错用户 |
用有权限的 HDFS 用户执行;调整目录属主/ACL;或将 ODS 路径放到可写目录 |
hive -e "$sql" 执行失败但脚本仍继续 |
未开启 set -e,错误未中断;或 hive 命令退出码未检查 |
echo $?;查看脚本日志 |
增加 set -euo pipefail;分段执行并检查返回码;记录失败表名 |
| 分区日期跑错(比如跑到前一天/时区错) | date -d "-1 day" 依赖系统时区与 GNU date |
date、date -d 输出核对 |
固化传参日期;或明确时区;容器环境用 busybox 需改写日期逻辑 |
ods_trade_order_product.orderid decimal(10,2) 与订单表 orderid int 不一致导致 join 异常 |
ODS 表 schema 不一致(同一业务主键类型漂移) | 对比两张表 DDL | 统一主键类型(推荐 bigint/string),ODS 尽量"原样+稳定",在 DWD 做规范化 |
其他系列
🚀 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案例 详解