可视化大数据——淘宝母婴购物数据【含详细代码】

一、项目介绍

1.1 项目背景

基于阿里母婴数据集和天池母婴交易历史数据,构建一个完整的母婴电商数据分析仓库,用于分析用户购买行为、商品销售趋势和用户画像。

1.2 数据源说明

|----------|------|---------|---------------------------------------------------|
| 数据源 | 文件格式 | 记录数 | 主要字段 |
| 阿里母婴数据集 | JSON | 953条 | user_id, birthday, gender |
| 天池母婴交易历史 | JSON | 29,971条 | user_id, auction_id, cat_id, cat1, buy_mount, day |

1.3 技术栈

  • 数据存储: HDFS + Hive

  • 计算引擎: Spark on Hive | MapReduce

  • 数据同步: DataX

  • 可视化: Quick BI + 花生壳(内网穿透)

  • 数据库: MySQL (用于报表存储)

二、数据仓库建模

2.1 整体架构图

2.2 各层设计理念

ODS层(Operation Data Store)

  • 设计理念: 贴源设计,保持数据原貌

  • 存储策略: 按日期分区,存储原始JSON数据

  • 目标: 支持数据回溯,减少对源系统的依赖

  • 数据位置:/mum_baby/ods/ods_mum_baby/(sample)sam_tianchi_mum_baby.txt

    /mum_baby/ods/ods_trade_history/(sample)sam_tianchi_mum_baby_trade_history.txt
    DWD层(Data Warehouse Detail)

  • 设计理念: 维度建模,构建事实表和维度表

  • 存储策略: ORC + Snappy压缩,按日期分区

  • 目标: 提供干净的、可复用的明细数据

  • 数据位置:/mum_baby/dwd/dwd_mum_baby

/mum_baby/dwd/dwd_trade
DIM层(Dimension)

  • 设计理念: 一致性维度,维度规范化

  • 存储策略: 全量存储,缓慢变化维处理

  • 目标: 提供统一的维度视图,支持多维度分析

  • 数据位置:/mum_baby/dim/dim_category

/mum_baby/dim/dim_user_baby
DWS层(Data Warehouse Service)

  • 设计理念: 面向主题,轻度聚合

  • 存储策略: ORC + Snappy压缩,按日期分区

  • 目标: 提高查询性能,支持即席查询

  • 数据位置:/mum_baby/dws/dws_user_trade_summary

/mum_baby/ads/ads_trade_by_baby_gender
ADS层(Application Data Store)

  • 设计理念: 面向应用,指标计算

  • 存储策略: 按业务需求分区

  • 目标: 直接支持报表和可视化

  • 数据位置:/mum_baby/ads/ads_trade_by_baby_gender

/mum_baby/ads/ads_trade_by_category

2.3 数据表结构设计

2.3.1 ODS层表结构

  • ods_mum_baby (母婴用户原始数据表)

    CREATE EXTERNAL TABLE ods_mum_baby (
    line STRING COMMENT '原始JSON数据'
    )
    LOCATION '/mum_baby/ods/ods_mum_baby';

设计思路: 直接存储原始JSON,便于数据追溯和重新处理。

  • ods_trade_history (交易历史原始数据表)

    CREATE EXTERNAL TABLE ods_trade_history (
    line STRING COMMENT '原始JSON数据'
    )
    LOCATION '/mum_baby/ods/ods_trade_history';

设计思路: 与母婴用户表设计一致,保持原始数据完整性。

2.3.2 DWD层表结构

  • dwd_mum_baby (母婴用户明细表)

    CREATE EXTERNAL TABLE dwd_mum_baby (
    user_id BIGINT COMMENT '用户ID',
    birthday STRING COMMENT '儿童生日(yyyyMMdd)',
    gender INT COMMENT '性别:0-女,1-男,2-未知'
    )
    PARTITIONED BY (dt STRING COMMENT '分区日期')
    STORED AS ORC
    LOCATION '/mum_baby/dwd/dwd_mum_baby'
    TBLPROPERTIES ("orc.compress"="snappy");

设计思路:

  1. 从JSON解析出结构化字段

  2. 按dt分区便于数据管理

  3. 使用ORC+Snappy压缩提高存储和查询效率

  • dwd_trade (交易明细事实表)

    CREATE EXTERNAL TABLE dwd_trade (
    user_id BIGINT COMMENT '用户ID',
    auction_id BIGINT COMMENT '商品ID',
    cat_id BIGINT COMMENT '类目ID',
    cat1 BIGINT COMMENT '一级类目ID',
    property STRING COMMENT '商品属性',
    buy_mount BIGINT COMMENT '购买数量',
    day STRING COMMENT '购买日期(yyyyMMdd)'
    )
    PARTITIONED BY (dt STRING COMMENT '分区日期')
    STORED AS ORC
    LOCATION '/mum_baby/dwd/dwd_trade'
    TBLPROPERTIES ("orc.compress"="snappy");

设计思路:

  1. 这是核心事实表,记录每笔交易详情

  2. 包含了用户、商品、类目、时间等多个维度

  3. 按dt(交易日期)分区,支持时间范围查询优化

2.3.3 DIM层表结构

  • dim_user_baby (用户-儿童维度表)

    CREATE EXTERNAL TABLE dim_user_baby (
    user_id BIGINT COMMENT '用户ID',
    birthday STRING COMMENT '儿童生日',
    gender INT COMMENT '性别',
    age_year INT COMMENT '年龄(年)'
    )
    STORED AS ORC
    LOCATION '/mum_baby/dim/dim_user_baby'
    TBLPROPERTIES ("orc.compress"="snappy");

维度设计思路:

  1. 用户ID: 维度主键,连接事实表

  2. 儿童生日: 可用于计算儿童年龄,分析不同年龄段消费特征

  3. 性别: 重要分析维度,分析男女童消费差异

  4. 年龄: 衍生维度,按年龄段分组分析

  • dim_category (商品类目维度表)

    CREATE EXTERNAL TABLE dim_category (
    cat_id BIGINT COMMENT '类目ID',
    cat1 BIGINT COMMENT '一级类目ID'
    )
    STORED AS ORC
    LOCATION '/mum_baby/dim/dim_category'
    TBLPROPERTIES ("orc.compress"="snappy");

维度设计思路:

  1. 类目层级: 支持类目层级分析(cat1为一级类目)

  2. 叶子类目标识: 区分是否是最终商品类目,便于不同粒度分析

  3. 可扩展性: 可扩展为多级类目(cat2, cat3等)

  • dim_date (时间维度表)

    CREATE EXTERNAL TABLE dim_date (
    date_id STRING COMMENT '日期 yyyyMMdd',
    week_id STRING COMMENT '周',
    week_day STRING COMMENT '星期几',
    day STRING COMMENT '日',
    month STRING COMMENT '月',
    quarter STRING COMMENT '季度',
    year STRING COMMENT '年',
    is_workday STRING COMMENT '是否工作日'
    )
    LOCATION '/mum_baby/dim/dim_date';

维度设计思路:

  1. 多层次时间粒度: 支持年、月、日、季度、周等多个时间维度分析

  2. 业务属性: 包含is_weekend等业务相关属性

  3. 完整性: 为所有交易日期生成记录,确保时间维度完整性

2.3.4 DWS层表结构

  • dws_user_summary (用户交易汇总宽表)

    CREATE EXTERNAL TABLE dws_user_summary (
    user_id BIGINT COMMENT '用户ID',
    total_trades BIGINT COMMENT '总交易次数',
    total_amount BIGINT COMMENT '总购买数量',
    avg_amount DOUBLE COMMENT '平均购买量',
    first_trade STRING COMMENT '首次交易日期',
    last_trade STRING COMMENT '最近交易日期'
    )
    PARTITIONED BY (dt STRING COMMENT '统计日期')
    STORED AS ORC
    LOCATION '/mum_baby/dws/dws_user_summary'
    TBLPROPERTIES ("orc.compress"="snappy");

设计思路:

  1. 用户粒度: 按用户聚合,形成用户画像基础

  2. 交易行为: 汇总交易次数、总购买量等核心指标

  3. 时间特征: 记录首次和最近交易,分析用户生命周期

2.3.5 ADS层表结构(export)

PS:这里由于刚开始在传ads层格式错误导致后面无法上传数据,所以直接新建了export层作为ads层

  • ads_trade_by_baby_gender (按儿童性别统计表)

    CREATE EXTERNAL TABLE ads_trade_by_baby_gender (
    gender INT COMMENT '性别',
    trade_count BIGINT COMMENT '交易次数',
    total_amount BIGINT COMMENT '总购买数量',
    dt STRING COMMENT '统计日期'
    )
    LOCATION '/mum_baby/export/ads_trade_by_baby_gender';

设计思路:

  1. 核心维度: 性别是最基本的用户画像维度

  2. 多重指标: 包含交易次数、购买数量、日期等多个角度

  3. 应用导向: 直接支持"性别消费分布"可视化图表

  • ads_trade_by_category (按商品类目统计表)

    CREATE EXTERNAL TABLE ads_trade_by_category (
    cat1 BIGINT COMMENT '一级类目',
    trade_count BIGINT COMMENT '交易次数',
    total_amount BIGINT COMMENT '总购买数量',
    dt STRING COMMENT '统计日期'
    )
    LOCATION '/mum_baby/export/ads_trade_by_category';

设计思路:

  1. 业务维度: 类目是电商核心分析维度

  2. 销售分析: 从交易次数、销售数量、两个维度分析

  3. 应用导向: 支持"热门类目排行榜"等可视化需求

  • dws_user_summary (用户交易汇总宽表)

    CREATE EXTERNAL TABLE IF NOT EXISTS dws_user_summary_text (
    user_id BIGINT,
    total_trades BIGINT,
    total_amount BIGINT,
    avg_amount DOUBLE,
    first_trade STRING,
    last_trade STRING,
    dt STRING
    )
    ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
    LOCATION '/mum_baby/export/dws_user_summary';

设计思路:

  1. 用户粒度: 按用户聚合,形成用户画像基础

  2. 交易行为: 汇总交易次数、总购买量等核心指标

  3. 时间特征: 记录首次和最近交易,分析用户生命周期

三、数据仓库建设

3.1 设置动态分区

复制代码
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;

3.2 从 ODS 到 DWD 的母婴信息表

复制代码
INSERT OVERWRITE TABLE dwd_mum_baby PARTITION (dt)
SELECT
    get_json_object(line, '$.user_id'),
    get_json_object(line, '$.birthday'),
    get_json_object(line, '$.gender')
FROM ods_mum_baby
WHERE line IS NOT NULL;

3.2 从 ODS 到 DWD 的交易表(按 day 分区)

复制代码
INSERT OVERWRITE TABLE dwd_trade PARTITION (dt)
SELECT
    get_json_object(line, '$.user_id'),
    get_json_object(line, '$.auction_id'),
    get_json_object(line, '$.cat_id'),
    get_json_object(line, '$.cat1'),
    get_json_object(line, '$.property'),
    get_json_object(line, '$.buy_mount'),
    get_json_object(line, '$.day'),
    get_json_object(line, '$.day') AS dt
FROM ods_trade_history
WHERE line IS NOT NULL;

3.3 创建用户维度表

复制代码
INSERT OVERWRITE TABLE dim_user_baby
SELECT
    user_id,
    birthday,
    gender,
    floor(datediff(current_date(), to_date(birthday, 'yyyyMMdd')) / 365) AS age_year
FROM dwd_mum_baby
WHERE birthday IS NOT NULL;

3.4 创建类目维度表

复制代码
INSERT OVERWRITE TABLE dim_category
SELECT DISTINCT
    cat_id,
    cat1
FROM dwd_trade
WHERE cat_id IS NOT NULL;

3.5 在ads层中将ORC格式转为文本文件

复制代码
-- 1. 导出按性别统计表
INSERT OVERWRITE DIRECTORY '/mum_baby/export/ads_trade_by_baby_gender'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
SELECT gender, trade_count, total_amount, dt 
FROM ads_trade_by_baby_gender;

-- 2. 导出按类目统计表
INSERT OVERWRITE DIRECTORY '/mum_baby/export/ads_trade_by_category'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE
SELECT cat1, trade_count, total_amount, dt 
FROM ads_trade_by_category;

-- 插入数据到宽表
INSERT OVERWRITE TABLE dws_user_summary_text
SELECT 
    user_id,
    COUNT(*) AS total_trades,
    SUM(buy_mount) AS total_amount,
    ROUND(AVG(CAST(buy_mount AS DOUBLE)), 2) AS avg_amount,
    MIN(day) AS first_trade,
    MAX(day) AS last_trade,
    'sample' AS dt
FROM dwd_trade
GROUP BY user_id;

四、数据可视化

4.1 在MySQL中创建目标表

复制代码
-- MySQL数据库:mum_baby
USE mum_baby;

-- 1. 按儿童性别统计表
CREATE TABLE `ads_trade_by_baby_gender` (
  `gender` int DEFAULT NULL COMMENT '性别:0-女,1-男,2-未知',
  `trade_count` bigint DEFAULT NULL COMMENT '交易次数',
  `total_amount` bigint DEFAULT NULL COMMENT '总购买数量',
  `dt` varchar(20) DEFAULT NULL COMMENT '统计日期'
) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='按儿童性别统计交易';

-- 2. 按商品类目统计表
CREATE TABLE `ads_trade_by_category` (
  `cat1` bigint DEFAULT NULL COMMENT '一级类目',
  `trade_count` bigint DEFAULT NULL COMMENT '交易次数',
  `total_amount` bigint DEFAULT NULL COMMENT '总购买数量',
  `dt` varchar(20) DEFAULT NULL COMMENT '统计日期'
) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='按商品类目统计交易';

-- 3. 用户交易汇总表(新增)
CREATE TABLE `dws_user_summary` (
  `user_id` bigint DEFAULT NULL COMMENT '用户ID',
  `total_trades` bigint DEFAULT NULL COMMENT '总交易次数',
  `total_amount` bigint DEFAULT NULL COMMENT '总购买数量',
  `avg_amount` double DEFAULT NULL COMMENT '平均购买量',
  `first_trade` varchar(20) DEFAULT NULL COMMENT '首次交易日期',
  `last_trade` varchar(20) DEFAULT NULL COMMENT '最近交易日期',
  `dt` varchar(20) DEFAULT NULL COMMENT '统计日期'
) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户交易汇总';

4.2 DataX配置文件

4.2.1 ads_trade_by_baby_gender.json (从HDFS导出到MySQL)

复制代码
{
    "job": {
        "setting": {
            "speed": {
                "channel": 1
            }
        },
        "content": [
            {
                "reader": {
                    "name": "hdfsreader",
                    "parameter": {
                        "path": "/mum_baby/export/ads_trade_by_gender/*",
                        "defaultFS": "hdfs://10.10.10.102:8020",
                        "column": [
                            {
                                "index": 0,
                                "type": "long"
                            },
                            {
                                "index": 1,
                                "type": "long"
                            },
                            {
                                "index": 2,
                                "type": "long"
                            },
                            {
                                "index": 3,
                                "type": "string"
                            }
                        ],
                        "fileType": "text",
                        "encoding": "UTF-8",
                        "fieldDelimiter": "\t"
                    }
                },
                "writer": {
                    "name": "mysqlwriter",
                    "parameter": {
                        "writeMode": "replace",
                        "username": "root",
                        "password": "DataX@123456",
                        "column": [
                            "gender",
                            "trade_count",
                            "total_amount",
                            "dt"
                        ],
                        "session": [
                            "set session sql_mode='ANSI'"
                        ],
                        "preSql": [
                            "truncate table ads_trade_by_baby_gender"
                        ],
                        "connection": [
                            {
                                "jdbcUrl": "jdbc:mysql://10.10.10.102:3306/mum_baby?useUnicode=true&characterEncoding=utf-8&useSSL=false",
                                "table": [
                                    "ads_trade_by_baby_gender"
                                ]
                            }
                        ]
                    }
                }
            }
        ]
    }
}

4.2.2 ads_trade_by_category.json

复制代码
{
    "job": {
        "setting": {
            "speed": {
                "channel": 1
            }
        },
        "content": [
            {
                "reader": {
                    "name": "hdfsreader",
                    "parameter": {
                        "path": "/mum_baby/export/ads_trade_by_category/*",
                        "defaultFS": "hdfs://10.10.10.102:8020",
                        "column": [
                            {
                                "index": 0,
                                "type": "long"
                            },
                            {
                                "index": 1,
                                "type": "long"
                            },
                            {
                                "index": 2,
                                "type": "long"
                            },
                            {
                                "index": 3,
                                "type": "string"
                            }
                        ],
                        "fileType": "text",
                        "encoding": "UTF-8",
                        "fieldDelimiter": "\t"
                    }
                },
                "writer": {
                    "name": "mysqlwriter",
                    "parameter": {
                        "writeMode": "replace",
                        "username": "root",
                        "password": "DataX@123456",
                        "column": [
                            "cat1",
                            "trade_count",
                            "total_amount",
                            "dt"
                        ],
                        "session": [
                            "set session sql_mode='ANSI'"
                        ],
                        "preSql": [
                            "truncate table ads_trade_by_category"
                        ],
                        "connection": [
                            {
                                "jdbcUrl": "jdbc:mysql://10.10.10.102:3306/mum_baby?useUnicode=true&characterEncoding=utf-8&useSSL=false",
                                "table": [
                                    "ads_trade_by_category"
                                ]
                            }
                        ]
                    }
                }
            }
        ]
    }
}

4.2.3 dws_user_summary.json

复制代码
{
    "job": {
        "setting": {
            "speed": {
                "channel": 1
            }
        },
        "content": [
            {
                "reader": {
                    "name": "hdfsreader",
                    "parameter": {
                        "path": "/mum_baby/export/dws_user_summary/*",
                        "defaultFS": "hdfs://10.10.10.102:8020",
                        "column": [
                            {
                                "index": 0,
                                "type": "long"
                            },
                            {
                                "index": 1,
                                "type": "long"
                            },
                            {
                                "index": 2,
                                "type": "long"
                            },
                            {
                                "index": 3,
                                "type": "double"
                            },
                            {
                                "index": 4,
                                "type": "string"
                            },
                            {
                                "index": 5,
                                "type": "string"
                            },
                            {
                                "index": 6,
                                "type": "string"
                            }
                        ],
                        "fileType": "text",
                        "encoding": "UTF-8",
                        "fieldDelimiter": "\t"
                    }
                },
                "writer": {
                    "name": "mysqlwriter",
                    "parameter": {
                        "writeMode": "replace",
                        "username": "root",
                        "password": "DataX@123456",
                        "column": [
                            "user_id",
                            "total_trades",
                            "total_amount",
                            "avg_amount",
                            "first_trade",
                            "last_trade",
                            "dt"
                        ],
                        "session": [
                            "set session sql_mode='ANSI'"
                        ],
                        "preSql": [
                            "truncate table dws_user_summary"
                        ],
                        "connection": [
                            {
                                "jdbcUrl": "jdbc:mysql://10.10.10.102:3306/mum_baby?useUnicode=true&characterEncoding=utf8&useSSL=false",
                                "table": [
                                    "dws_user_summary"
                                ]
                            }
                        ]
                    }
                }
            }
        ]
    }
}

五、项目展示

六、项目出现的问题记录【附】

6.1 项目过程

经历了四次建表建仓,如图,只有最后一次成功了,接下来将说一下具体过程:

6.1.1 第一次尝试失败总结

在第一次进行传输时,其实是最后有导出数据,但是由于建表到建仓在insert那里有许多数据没有传上去,导致最后只有一个总体记录的数据集,如下:

只能做出来一个卡片,先大体跑了一遍流程,唯一的卡片如下:

6.1.2 第二次尝试失败总结

第一次做出来一个卡片我想让ai帮忙再多一些数据,但是没想到直接给我新建仓了

在进行第二次传数据时,但是由于是以数组存放json文件的,再加上之前还加了一些字段,导致过程中出现了很多很多问题,尤其有的数据传成功了,有的不行,导致整个人很乱,于是第二次也告别失败了

6.1.3 第三次尝试失败总结

这次先用python把csv文件转成了json文件,不添加任何字段,去掉了json文件的 '[' , ']',然后做到一半看到了老师发过来处理好的json格式的文档,于是怕自己转的有问题,果断放弃当前自己处理的数据,用老师的文档做,才有了后面第四次的成功

6.2 项目踩坑记录

6.2.1 建表

先说建表,本以为用ai生成就行,然后历经几次失败,才发现是需要知道我要什么数据才行,还是需要了解里面的每一个字段是具体什么用处的才行,建表也得从简单的做起,不能直接暴力的把两个表直接合并在一起,还是得微微的熟悉下

6.2.2 日志

主要也是在第一次尝试的时候,想贴合已有的jar包,在用python把csv文件转成json文件还多加了一些字段,导致最后把自己都绕进去了,于是就放弃了这个想法

所以关于日志这个事情,我想自己用java写怕来不及,主要也是ai也不是很可靠,我怕出现我搞不定的错误,一直卡在那里没有进度,所以暂时搁置了,只是想先从简单的做起,然后有了这次经验之后再慢慢深入了解

6.2.3 数据仓库建设

其实在hive里面建表还是很好建表的,关键是插入这个数据就显得问题很多了,归根究底前几次都是因为json格式导致最后数据经常失败,才不太顺利

6.2.4 mysql

这里由于最开始我的c盘,d盘满了,中间用一天时间清理c盘,d盘,上传几十个G的文件,导致没有听mysql的登录密码是root,然后问ai找那个初始化密码,发现有的时候登录成功,有的时候登录不成功,改密码那里也是卡了一段时间,改无密码登录,然后后面发现无密码登录之后他会觉得不安全,会限制你,然后把密码重置了一遍'DataX@123456'才把密码改过来的

6.2.5 DataX配置文件

然后中间DataX的配置文件那里,首先就是要保证字段中不能用int类型,之前就是这个问题,只能把int改成long,然后就是路径,ip和当前ip是否一致等

6.3 心得体会

总而言之,做完发现也不是很复杂,但是细节很多,还是要先把基础掌握好,再深入去了解

虽然很基础,做出来也是普通的样子,但是还是很有成就感

相关推荐
春日见5 小时前
拉取与合并:如何让个人分支既包含你昨天的修改,也包含 develop 最新更新
大数据·人工智能·深度学习·elasticsearch·搜索引擎
主机哥哥5 小时前
还不会部署OpenClaw?阿里云推出五种OpenClaw快速部署方案
阿里云·云计算
Elastic 中国社区官方博客7 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索
YangYang9YangYan8 小时前
2026中专大数据与会计专业数据分析发展路径
大数据·数据挖掘·数据分析
W133309089078 小时前
工业大数据方向,CDA证书和工业数据工程师证哪个更实用?
大数据
Elastic 中国社区官方博客9 小时前
Elasticsearch:交易搜索 - AI Agent builder
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索
SQL必知必会10 小时前
使用 SQL 进行 RFM 客户细分分析
大数据·数据库·sql
YangYang9YangYan10 小时前
2026大专大数据技术专业学数据分析指南
大数据·数据挖掘·数据分析