TL;DR
- 场景:电商离线数仓 ODS 层,MySQL 7 张表每日抽取到 HDFS 并按 dt 分区给 Hive 加载。
- 结论:小表全量 + 大表按可分辨新增字段做增量;DataX channel=1 控小文件;HDFS 先建分区目录再跑作业。
- 产出:可复用 DataX JSON 模板(mysqlreader→hdfswriter)+ 每日调度脚本骨架 + 分区加载命令。
数据导入
已经确定的事情:DataX、导出7张表的数据。
MySQL导出:全量导出、增量导出(导出前一天的数据)
业务数据保存在MySQL中,每日凌晨上一天的表数据:
- 表数据量少,采用全量方式导出MySQL
- 表数据量多,而且根据字段能区分出每天新增数据,采用增量的方式导出MySQL
3张增量表:
- 订单表 wzk_trade_orders
- 订单产品表 wzk_order_produce
- 产品信息表 wzk_product_info
4张全量表:
- 产品分量表 wzk_product_category
- 商家店铺表 wzk_shops
- 商家地域组织表 wzk_shop_admin_org
- 支付方式表 wzk_payment
数据库导入
上述的内容中,可以先把表构建出来,然后生成一批数据,用于测试,我这里就导入生成好的数据了。
业务需求
电商系统业务中最关键的业务,电商的运营活动都是围绕这个主题展开。 选取的指标包括:订单数、商品数、支付金额,对这些指标按销售区域、商品类型分析。
在大数据的分析中,"电商核心交易"是指电商平台上所有与商品交易相关的核心行为和交易数据的集合。具体来说,核心交易涵盖了商品的浏览、加购物车、下单、支付、发货、收货等一系列行为,它们直接影响电商平台的运营效率、用户体验和商业价值。
需求板块
电商平台的核心交易可以分为以下几个主要环节,每个环节都涉及大量数据的收集、存储和分析:
- 商品浏览:用户浏览商品的行为数据,例如用户查看了哪些商品、查看时长、是否点击了相关广告或推荐商品等。这些数据能够帮助平台了解用户的兴趣点,进而优化商品推荐和个性化营销策略。
- 加入购物车:用户将商品添加到购物车中的行为。通过分析购物车中的商品,可以获取用户的购买意图和倾向,帮助商家调整商品定价、库存和促销策略。
- 下单:用户在电商平台上完成的订单生成行为。包括订单的创建、订单内容、用户的收货地址、选择的支付方式等数据。订单数据是电商交易中的核心,通常涉及大量的数据信息,要求系统能够高效地处理和存储。
- 支付:支付是交易中至关重要的环节,支付数据可以通过支付方式、支付成功与否、支付金额、支付时间等维度进行分析。这部分数据可以帮助平台评估不同支付方式的受欢迎程度,并进行相应的优化。
- 发货:商品发货数据记录了商家发货的时间、物流公司、物流单号等信息。通过对发货数据的分析,可以判断出物流时效、发货效率等关键指标,进一步优化供应链和物流流程。
- 收货和评价:用户收到商品后的评价、退换货行为等。评价数据不仅反映了商品的质量和用户满意度,还对后续的购买决策产生影响。此外,退换货数据也能够反映出商品质量问题和物流中的痛点。
全量数据导入
- MySQL => HDFS => Hive
- 每日加载全量数据,形成新的分区
- MySQL Reader => HDFS Writer
产品分类表
vim /opt/wzk/datax/product_category.json
写入的内容如下所示:
- 数据量小的表没有必要使用多个channel,使用多个channel会生成多个小文件
- 执行命令之前要在HDFS上创建对应的目录:/user/data/trade.db/product_category/dt=yyyy-mm-dd
{
"job": {
"setting": {
"speed": {
"channel": 1
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "hive",
"password": "hive@wzk.icu",
"column": [
"catId",
"parentId",
"catName",
"isShow",
"sortNum",
"isDel",
"createTime",
"level"
],
"connection": [
{
"table": [
"wzk_product_category"
],
"jdbcUrl": [
"jdbc:mysql://h122.wzk.icu:3306/ebiz"
]
}
]
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://h121.wzk.icu:9000",
"fileType": "text",
"path": "/user/data/trade.db/product_category/dt=$do_date",
"fileName": "product_category_$do_date",
"column": [
{
"name": "catId",
"type": "INT"
},
{
"name": "parentId",
"type": "INT"
},
{
"name": "catName",
"type": "STRING"
},
{
"name": "isShow",
"type": "TINYINT"
},
{
"name": "sortNum",
"type": "INT"
},
{
"name": "isDel",
"type": "TINYINT"
},
{
"name": "createTime",
"type": "STRING"
},
{
"name": "level",
"type": "TINYINT"
}
],
"writeMode": "append",
"fieldDelimiter": ","
}
}
}
]
}
}
写入的结果如下图:
加载数据的过程如下:
do_date='2020-07-01'
# 创建目录
hdfs dfs -mkdir -p /user/data/trade.db/product_category/dt=$do_date
# 数据迁移
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/product_category.json
# 加载数据
# hive 还没有表,后续再执行
hive -e "alter table ods.ods_trade_product_category add partition(dt='$do_date')"
对应的截图如下所示:
DataX将MySQL数据加载到HDFS上:
商家店铺表
wzk_shops => ods.ods_trade_shops
vim /opt/wzk/datax/shops.json
创建的内容:
{
"job": {
"setting": {
"speed": {
"channel": 1
},
"errorLimit": {
"record": 0
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "hive",
"password": "hive@wzk.icu",
"column": [
"shopId",
"userId",
"areaId",
"shopName",
"shopLevel",
"status",
"createTime",
"modifyTime"
],
"connection": [
{
"table": [
"wzk_shops"
],
"jdbcUrl": [
"jdbc:mysql://h122.wzk.icu:3306/ebiz"
]
}
]
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://h121.wzk.icu:9000",
"fileType": "text",
"path": "/user/data/trade.db/shops/dt=$do_date",
"fileName": "shops_$do_date",
"column": [
{
"name": "shopId",
"type": "INT"
},
{
"name": "userId",
"type": "INT"
},
{
"name": "areaId",
"type": "INT"
},
{
"name": "shopName",
"type": "STRING"
},
{
"name": "shopLevel",
"type": "TINYINT"
},
{
"name": "status",
"type": "TINYINT"
},
{
"name": "createTime",
"type": "STRING"
},
{
"name": "modifyTime",
"type": "STRING"
}
],
"writeMode": "append",
"fieldDelimiter": ","
}
}
}
]
}
}
对应的截图如下所示:
数据加载执行如下指令:
do_date='2020-07-02'
# 创建目录
hdfs dfs -mkdir -p /user/data/trade.db/shops/dt=$do_date
# 数据迁移
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/shops.json
# 加载数据
# hive中还没有表 后续再执行
hive -e "alter table ods.ods_trade_shops add
partition(dt='$do_date')"
DataX 数据库数据导入到 HDFS 结果如下:
商家地域组织表
wzk_shop_admin_org => ods.ods_trade_shop_admin_org
vim /opt/wzk/datax/shop_org.json
编写的内容如下所示:
{
"job": {
"setting": {
"speed": {
"channel": 1
},
"errorLimit": {
"record": 0
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "hive",
"password": "hive@wzk.icu",
"column": [
"id",
"parentId",
"orgName",
"orgLevel",
"isDelete",
"createTime",
"updateTime",
"isShow",
"orgType"
],
"connection": [
{
"table": [
"wzk_shop_admin_org"
],
"jdbcUrl": [
"jdbc:mysql://h122.wzk.icu:3306/ebiz"
]
}
]
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://h121.wzk.icu:9000",
"fileType": "text",
"path": "/user/data/trade.db/shop_org/dt=$do_date",
"fileName": "shop_admin_org_$do_date.dat",
"column": [
{
"name": "id",
"type": "INT"
},
{
"name": "parentId",
"type": "INT"
},
{
"name": "orgName",
"type": "STRING"
},
{
"name": "orgLevel",
"type": "TINYINT"
},
{
"name": "isDelete",
"type": "TINYINT"
},
{
"name": "createTime",
"type": "STRING"
},
{
"name": "updateTime",
"type": "STRING"
},
{
"name": "isShow",
"type": "TINYINT"
},
{
"name": "orgType",
"type": "TINYINT"
}
],
"writeMode": "append",
"fieldDelimiter": ","
}
}
}
]
}
}
对应的截图如下所示:
数据加载脚本:
do_date='2020-07-01'
# 创建目录
hdfs dfs -mkdir -p /user/data/trade.db/shop_org/dt=$do_date
# 数据迁移
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/shop_org.json
# 加载数据
# hive中还没有表 后续再执行
hive -e "alter table ods.ods_trade_shop_admin_org add
partition(dt='$do_date')"
写入的内容如下所示,从数据库将数据加载到HDFS中:
支付方式表
wzk_payments => ods.ods_trade_payments
vim /opt/wzk/datax/payments.json
对应的内容如下:
{
"job": {
"setting": {
"speed": {
"channel": 1
},
"errorLimit": {
"record": 0
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "hive",
"password": "hive@wzk.icu",
"column": [
"id",
"payMethod",
"payName",
"description",
"payOrder",
"online"
],
"connection": [
{
"table": [
"wzk_payments"
],
"jdbcUrl": [
"jdbc:mysql://h122.wzk.icu:3306/ebiz"
]
}
]
}
},
"writer": {
"name": "hdfswriter",
"parameter": {
"defaultFS": "hdfs://h121.wzk.icu:9000",
"fileType": "text",
"path": "/user/data/trade.db/payments/dt=$do_date",
"fileName": "payments_$do_date.dat",
"column": [
{
"name": "id",
"type": "INT"
},
{
"name": "payMethod",
"type": "STRING"
},
{
"name": "payName",
"type": "STRING"
},
{
"name": "description",
"type": "STRING"
},
{
"name": "payOrder",
"type": "INT"
},
{
"name": "online",
"type": "TINYINT"
}
],
"writeMode": "append",
"fieldDelimiter": ","
}
}
}
]
}
}
对应的截图如下:
数据导入的过程如下:
do_date='2020-07-01'
# 创建目录
hdfs dfs -mkdir -p /user/data/trade.db/payments/dt=$do_date
# 数据迁移
python $DATAX_HOME/bin/datax.py -p "-Ddo_date=$do_date" /opt/wzk/datax/payments.json
# 加载数据
# hive还没有表 后续再执行
hive -e "alter table ods.ods_trade_payments add
partition(dt='$do_date')"
从MySQL中将数据加载到HDFS中:
错误速查
| 症状 | 根因定位 | 修复 |
|---|---|---|
| DataX 写入失败:HDFS No such file or directory / Path does not exist | 分区目录未提前创建(你当前依赖手工 hdfs dfs -mkdir -p) | 看 DataX Job 日志中 hdfswriter 异常;同时 hdfs dfs -ls /user/data/trade.db/.../dt=...运行前统一创建目录;把“mkdir + datax + add partition”封装成同一个日调脚本 |
| Hive add partition 报错:table not found / database not found | ODS 表尚未创建(你多处写了“hive 还没有表 后续再执行”) | hive -e "show tables in ods" / describe formatted ods.xxx先建库建表(含 dt 分区字段),再执行 add partition;或改为 MSCK REPAIR TABLE(前提是分区目录规范) |
| 每日跑完 HDFS 出现大量小文件 | channel>1 或多次 append 叠加;并行写导致文件碎片 | hdfs dfs -count -q -h 看文件数;NameNode 压力迹象小表固定 channel=1;大表控制并行与切分;必要时落地后做合并(如 Hive insert overwrite/compaction 思路) |
| 字段错位/解析失败(CSV 逗号冲突) | fieldDelimiter: "," 但字段内容包含逗号、换行或未转义 | 抽查 HDFS 文件;对比 MySQL 源字段是否有文本类含逗号更换分隔符(如 \001);或输出为更稳定格式(如 ORC/Parquet,需引入对应 writer/落库方式) |
| 增量表重复/漏数 | “导出前一天数据”口径依赖时间字段;时区/延迟写入/更新覆盖导致边界问题 | 对比 MySQL where 条件与 Hive 分区 dt;抽样核对昨日数据是否全覆盖增量用闭开区间(>=start and <end);引入延迟窗口(T-1/T-2 补拉);对更新型表增加 upsert 处理策略 |
| 数据类型不匹配导致 Hive 查询异常 | DataX writer 中 type 仅用于写文件,Hive 表类型与文件内容不一致(尤其时间/字符串/数值) | describe formatted 对比 Hive schema;抽查 HDFS 文件实际值统一“文件字段顺序 + Hive 列类型 + 分隔符”;时间字段明确格式(yyyy-MM-dd HH:mm:ss 等) |
| MySQL 读取慢/锁表风险 | 全量抽取对大表压力大;未做分片/条件过滤 | MySQL 慢查询/负载;DataX reader 吞吐低大表走增量;reader 增加条件与切分;在业务低峰跑;必要时走从库或快照机制 |
| HDFS 文件名冲突或追加混乱 | writeMode: append + 固定 fileName,重复跑会叠加数据 | 同一 dt 目录下 hdfs dfs -ls 出现多次同名前缀文件约束重跑策略:重跑前清理分区目录;或按时间戳/批次号生成 fileName;或改覆盖写并确保幂等 |
其他系列
🚀 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案例 详解