假日出行大数据分析平台

一、实验概述

1.1 实验目的

本次实验旨在搭建大数据处理平台,通过模拟生成假日出行数据,构建数据仓库(Hive)进行分层处理,并使用 AJ-Report 进行数据可视化展示,最终实现假日出行数据的分析与报表展示。

1.2 实验环境

组件 版本 说明
操作系统 Ubuntu 22.04 虚拟机
Hadoop 3.1.3 HDFS + YARN
Zookeeper 3.5.7 集群协调
Kafka 3.0.0 消息队列
Hive 3.1.2 数据仓库(Hive-on-Spark)
MySQL 8.4 关系型数据库
DataX - 数据同步工具
Maxwell 1.29.2 实时 Binlog 采集
Flume 1.11.0 日志采集
AJ-Report 1.4.1 可视化报表
JDK 1.8 Java 运行环境

1.3 网络配置

  • 主机名:hadoop102
  • IP 地址:192.168.56.101
  • 伪分布式环境

二、基础环境搭建

2.1 Hadoop 配置

core-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://hadoop102:8020</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/opt/module/hadoop-3.1.3/data</value>
    </property>
</configuration>

hdfs-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

yarn-site.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>hadoop102</value>
    </property>
    <property>
        <name>yarn.nodemanager.env-whitelist</name>
        <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services.mapreduce_shuffle.class</name>
        <value>org.apache.hadoop.mapred.ShuffleHandler</value>
    </property>
</configuration>

2.2 Kafka 配置(server.properties)

# broker 编号
broker.id=0
# 端口
listeners=PLAINTEXT://0.0.0.0:9092
# 日志目录
log.dirs=/opt/module/kafka-3.0.0/data
# Zookeeper 连接
zookeeper.connect=127.0.0.1:2181
# 分区数
num.partitions=3
# 副本数
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
advertised.listeners=PLAINTEXT://192.168.56.101:9092

2.3 Hive 配置(hive-site.xml)

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>javax.jdo.option.ConnectionURL</name>
        <value>jdbc:mysql://127.0.0.1:3306/hive?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;characterEncoding=UTF-8</value>
    </property>
    <property>
        <name>javax.jdo.option.ConnectionDriverName</name>
        <value>com.mysql.cj.jdbc.Driver</value>
    </property>
    <property>
        <name>javax.jdo.option.ConnectionUserName</name>
        <value>root</value>
    </property>
    <property>
        <name>javax.jdo.option.ConnectionPassword</name>
        <value>123456</value>
    </property>
    <property>
        <name>hive.metastore.warehouse.dir</name>
        <value>/user/hive/warehouse</value>
    </property>
    <property>
        <name>hive.cli.print.current.db</name>
        <value>true</value>
    </property>
</configuration>

2.4 AJ-Report 配置(包括 bootstrap.yml,JAR)

spring:
  profiles:
    active: prod
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/aj_report?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    type: com.zaxxer.hikari.HikariDataSource

2.5 服务启动验证

成功启动的服务:

  • Hadoop HDFS(NameNode、DataNode、SecondaryNameNode)
  • Hadoop YARN(ResourceManager、NodeManager)
  • Zookeeper(QuorumPeerMain)
  • Kafka
  • Hive Metastore
  • MySQL
  • AJ-Report(端口 9095)

(截图)


三、模拟数据生成

3.1 数据表结构

users(用户表)

CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `age` int NOT NULL,
  `mobile` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

aoi(景区表)

CREATE TABLE `aoi` (
  `id` int NOT NULL,
  `name` varchar(100) NOT NULL,
  `city` varchar(50) NOT NULL,
  `type` varchar(10) NOT NULL,
  `lng` varchar(20) NOT NULL,
  `lat` varchar(20) NOT NULL,
  `lng_wgs` varchar(20) NOT NULL,
  `lat_wgs` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

3.2 模拟数据生成脚本

用户数据生成(insertUser.py)

import mysql.connector
import random
import faker

conn = mysql.connector.connect(
    host="localhost",
    user="root",
    password="123456",
    database="travel"
)
cursor = conn.cursor()

# 创建用户表
create_table_sql = """
create table if not exists users(
    id int auto_increment primary key,
    name varchar(50) not null,
    age int not null,
    mobile varchar(50) not null 
)
"""
cursor.execute(create_table_sql)

# 生成1000条虚拟用户数据
fake = faker.Faker()
users_data = [(fake.name(), random.randint(18, 80), fake.phone_number()) for _ in range(1000)]
insert_sql = "insert into users (name,age,mobile) values (%s,%s,%s)"
cursor.executemany(insert_sql, users_data)
conn.commit()
cursor.close()
conn.close()

3.3 数据生成结果展示

image


四、数仓构建

4.1 数仓分层架构

ODS 层(原始数据层)
  ├── ods_users_full          -- 用户表
  ├── ods_aoi_full            -- 景区表
  ├── ods_user_travels_inc    -- 用户出行增量表
  └── ods_user_activities_inc -- 用户活动增量表

DWD 层(明细数据层)
  ├── dwd_users               -- 用户维度表(维度退化)
  ├── dwd_user_travels        -- 出行事实表
  └── dwd_user_activities     -- 活动事实表

DIM 层(维度层)
  ├── dim_area                -- 区域维度表
  └── dim_date_2023           -- 日期维度表

DWS 层(服务数据层)
  └── dws_aoi_city            -- 按城市聚合景区指标

ADS 层(应用数据层)
  └── ads_aoi_stat            -- 各城市景区统计

4.2 数据同步

DataX 全量同步

  • users 表同步:MySQL → HDFS(/travel/db/users_full)
  • aoi 表同步:MySQL → HDFS(/travel/db/aoi_full)

DataX 配置(users.json):

{
    "job": {
        "content": [{
            "reader": {
                "name": "mysqlreader",
                "parameter": {
                    "column": ["id", "name", "age", "mobile"],
                    "connection": [{
                        "jdbcUrl": ["jdbc:mysql://hadoop102:3306/travel?useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf-8"],
                        "table": ["users"]
                    }],
                    "password": "123456",
                    "username": "root"
                }
            },
            "writer": {
                "name": "hdfswriter",
                "parameter": {
                    "column": [
                        {"name": "id", "type": "bigint"},
                        {"name": "name", "type": "string"},
                        {"name": "age", "type": "string"},
                        {"name": "mobile", "type": "string"}
                    ],
                    "compress": "gzip",
                    "defaultFS": "hdfs://hadoop102:8020",
                    "fieldDelimiter": "\t",
                    "fileType": "text",
                    "path": "/travel/db/users_full",
                    "writeMode": "truncate"
                }
            }
        }]
    }
}

Maxwell 实时增量采集

  • 配置 Maxwell 监听 MySQL binlog
  • 将增量数据发送到 Kafka topic incdb0710
  • 捕获 user_travels 和 user_activities 表的变更

Maxwell 配置(config.properties):

producer=kafka
kafka.bootstrap.servers=hadoop102:9092,hadoop103:9092,hadoop104:9092
kafka_topic=incdb0710
host=hadoop102
user=maxwell
password=123456
producer_partition_by=primary_key
client_id=maxwell_first
kafka.compression.type=snappy
kafka.retries=0
kafka.acks=1

Flume 日志采集

  • 从 Kafka 消费数据写入 HDFS
  • 自定义拦截器(TimestampAndTableNameInterceptor)提取表名和时间戳

Flume 配置(travel.conf):

# source
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.kafka.bootstrap.servers = 192.168.10.102:9092
a1.sources.r1.kafka.topics = incdb9
a1.sources.r1.kafka.consumer.group.id = flume9

# channel
a1.channels.c1.type = file
a1.channels.c1.checkpointDir = /opt/module/flume/checkpoint/travel
a1.channels.c1.dataDirs = /opt/module/flume/data/travel/

# interceptor
a1.sources.r1.interceptors.i1.type = cn.wolfcode.flume.interceptor.TimestampAndTableNameInterceptor$MyBuilder

# sink
a1.sinks.k1.type = hdfs
a1.sinks.k1.hdfs.path = /travel/db/%{tableName}_inc/%Y-%m-%d
a1.sinks.k1.hdfs.fileType = CompressedStream
a1.sinks.k1.hdfs.codeC = gzip

4.3 数仓各层表清单

ODS 层

ods_users_full           -- 用户全量数据
ods_aoi_full             -- 景区全量数据
ods_user_travels_inc     -- 用户出行增量数据
ods_user_activities_inc  -- 用户活动增量数据

DWD 层

dwd_users                -- 用户维度表
dwd_user_travels         -- 出行事实表
dwd_user_activities      -- 活动事实表

DWS 层

dws_aoi_city             -- 按城市聚合景区指标

ADS 层

ads_aoi_stat             -- 各城市景区统计(最终结果)

4.4 ADS 层数据导出

使用 DataX 将 ADS 层数据从 HDFS 导出到 MySQL,供 AJ-Report 使用:

{
    "job": {
        "content": [{
            "reader": {
                "name": "hdfsreader",
                "parameter": {
                    "path": "/travel/ads/aoi_stat/*",
                    "defaultFS": "hdfs://hadoop102:8020",
                    "fileType": "text",
                    "column": [{"index": 0, "type": "string"}, {"index": 1, "type": "long"}],
                    "fieldDelimiter": "\t"
                }
            },
            "writer": {
                "name": "mysqlwriter",
                "parameter": {
                    "writeMode": "replace",
                    "username": "root",
                    "password": "123456",
                    "connection": [{
                        "jdbcUrl": "jdbc:mysql://127.0.0.1:3306/travel?useSSL=false&allowPublicKeyRetrieval=true",
                        "table": ["ads_aoi_stat"]
                    }],
                    "column": ["city", "aoi_count"]
                }
            }
        }]
    }
}

4.5 ADS 层最终数据

SELECT * FROM ads_aoi_stat ORDER BY aoi_count DESC;
城市 景区数量(个)
山东 1227
浙江 798
四川 725
江苏 615
安徽 605
广西 557
河南 495
陕西 460
新疆 440
江西 421
河北 420
湖北 409
贵州 409
黑龙江 395
内蒙古 380
福建 375
云南 344
辽宁 323
甘肃 323
湖南 289
北京 245
吉林 231
重庆 224
山西 218
上海 111
西藏 116
青海 115
天津 97
宁夏 96
广州 66
海南 64
惠州 36
韶关 31
佛山 28
清远 26
东莞 25
中山 19
江门 18
深圳 16
湛江 16
河源 15
肇庆 15
茂名 14
云浮 12
阳江 12
梅州 32
潮州 8
珠海 8
汕尾 9
汕头 10
揭阳 10
3A 10
4A 6
31.2769932964777 1
A 1

五、报表开发

5.1 AJ-Report 部署

image

5.2 大屏报表设计

大屏包含以下 5 个图表组件:

1. 柱状图 — 各城市景区数量 TOP15

SELECT city, aoi_count FROM ads_aoi_stat ORDER BY aoi_count DESC LIMIT 15;
  • X 轴:city(城市)
  • Y 轴:aoi_count(景区数量,求和)
  • 说明:展示景区数量排名前 15 的城市

image
image

2. 中国地图 — 全国景区分布热力图

SELECT city, aoi_count FROM ads_aoi_stat WHERE city IN ('北京','天津','上海','重庆','河北','山西','辽宁','吉林','黑龙江','江苏','浙江','安徽','福建','江西','山东','河南','湖北','湖南','广东','海南','四川','贵州','云南','陕西','甘肃','青海','台湾','内蒙古','广西','西藏','宁夏','新疆') ORDER BY aoi_count DESC;
  • 维度:city(城市)
  • 指标:aoi_count(景区数量,求和)
  • 说明:在地图上直观展示各省市景区分布密度

image
image

3. 饼图 — 景区数量占比 TOP10

SELECT city, aoi_count FROM ads_aoi_stat ORDER BY aoi_count DESC LIMIT 10;
  • 维度:city(城市)
  • 指标:aoi_count(景区数量,求和)
  • 说明:展示 TOP10 城市景区数量的占比分布

image
image

4. 漏斗图 — 各城市景区数量漏斗

SELECT city, aoi_count FROM ads_aoi_stat ORDER BY aoi_count DESC LIMIT 10;
  • 维度:city(城市)
  • 指标:aoi_count(景区数量,求和)
  • 说明:以漏斗图形式展示 TOP10 城市景区数量递进关系

image
image

5. 词云图 — 城市景区数量词云

SELECT city AS name, aoi_count AS value FROM ads_aoi_stat ORDER BY aoi_count DESC;
  • 名称字段:name(城市名)
  • 值字段:value(景区数量)
  • 说明:以词云形式展示各城市景区数量分布

image
image

5.3 大屏报表完整预览截图

image

5.4 数据分析结论

  1. 山东省景区数量最多(1227 个),远超其他省份,是旅游资源最丰富的地区
  2. 浙江(798 个)、四川(725 个)、江苏(615 个)位列第二梯队,均为旅游大省
  3. 东部沿海省份(山东、浙江、江苏、福建)景区数量普遍高于中西部地区
  4. 西部省份中,四川(725 个)、陕西(460 个)、新疆(440 个)表现突出
  5. 一线城市中,北京(245 个)景区数量最多,其次是重庆(224 个)、上海(111 个)

六、实验总结

6.1 遇到的问题及解决方案

  1. YARN 启动失败:yarn-site.xml 多根节点问题 → 合并为单一 <configuration> 根节点
  2. DataX 同步缺失 fileType 参数 → 添加 "fileType": "text" 配置
  3. AJ-Report 连接 MySQL 失败:配置文件中的远程 IP 未修改 → 修改 JAR 包内 bootstrap.yml 为 127.0.0.1
  4. Kafka 连接失败:缺少 advertised.listeners 配置 → 添加 advertised.listeners=PLAINTEXT://192.168.56.101:9092

6.2 关键技术点

  • 使用 Hive 数仓分层(ODS→DWD→DIM→DWS→ADS)实现数据逐层清洗和聚合
  • DataX 实现 MySQL 到 HDFS 的全量数据同步
  • Maxwell + Kafka + Flume 实现 MySQL 增量数据的实时捕获和 HDFS 存储
  • 自定义 Flume 拦截器解析表名和时间戳
  • AJ-Report 制作多图表大屏实现数据可视化

6.3 心得体会

本次实验让我完整经历了大数据分析的全流程。环境搭建的排过程加深了我对分布式组件配置原理的理解;数仓分层设计(ODS→DWD→DWS→ADS)使我认识到数据逐层解耦和复用的重要性,显著提升了查询效率和维护性。利用 Maxwell + Kafka + Flume 实现实时增量同步,让我直观感受到“实时数仓”的技术魅力,也体会到数据一致性保障的难度。最后,通过 AJ-Report 将统计数据可视化呈现,让我看到数据驱动决策的实际价值。这次实践不仅锻炼了动手能力,更构建了我对大数据工程体系的整体认知,为后续深入学习奠定了扎实基础。

posted @ 2026-10-21 16:55  Hsibo  阅读(9)  评论(0)    收藏  举报