快速实验篇(A4)Hive 数据仓库进阶:全站点干旱事件识别与多维统计分析

小肥柴的Hadoop之旅 快速实验篇(A4)Hive 数据仓库进阶:全站点干旱事件识别与多维统计分析

    • 目录
    • 前言
    • [0. 背景知识:从单站点分析到全站点画像](#0. 背景知识:从单站点分析到全站点画像)
    • [1. 任务概述与目标](#1. 任务概述与目标)
    • [2. 磁盘危机:从 Spill 失败到 LVM 扩容(工程实战)](#2. 磁盘危机:从 Spill 失败到 LVM 扩容(工程实战))
      • [2.1 资源基础:可供 Hive 消耗的内存上限](#2.1 资源基础:可供 Hive 消耗的内存上限)
      • [2.1 故障现象](#2.1 故障现象)
      • [2.2 初步排查:HDFS 报告显示空间充裕](#2.2 初步排查:HDFS 报告显示空间充裕)
      • [2.3 深入定位:根分区只有 14 GB](#2.3 深入定位:根分区只有 14 GB)
      • [2.4 无效的清理尝试](#2.4 无效的清理尝试)
      • [2.5 根本原因定位:LVM 闲置了 14 GB](#2.5 根本原因定位:LVM 闲置了 14 GB)
      • [2.6 根本解决:LVM 在线扩容](#2.6 根本解决:LVM 在线扩容)
      • [.7 经验教训](#.7 经验教训)
    • [3. 环境与资源背景](#3. 环境与资源背景)
    • [4. 分析查询与结果](#4. 分析查询与结果)
      • [4.1 会话参数设置](#4.1 会话参数设置)
      • [4.2 步骤一:站点干旱日统计](#4.2 步骤一:站点干旱日统计)
      • [4.3 步骤二:干旱标记表(全量)](#4.3 步骤二:干旱标记表(全量))
      • [4.4 步骤三:干旱事件识别(gaps-and-islands)](#4.4 步骤三:干旱事件识别(gaps-and-islands))
        • [执行机制深度分析:为什么两个 Stage 都有 Map 和 Reduce?](#执行机制深度分析:为什么两个 Stage 都有 Map 和 Reduce?)
        • [慢启动机制:Map 未到 100%,Reduce 为何已有进度?](#慢启动机制:Map 未到 100%,Reduce 为何已有进度?)
      • [4.5 步骤四:事件级统计](#4.5 步骤四:事件级统计)
      • [4.6 步骤五:全站点干旱画像(稀疏补零)](#4.6 步骤五:全站点干旱画像(稀疏补零))
      • [4.7 步骤六:排名与分布分析](#4.7 步骤六:排名与分布分析)
        • [step 1:总体概览](#step 1:总体概览)
        • [step 2:最长连续干旱天数 Top 20](#step 2:最长连续干旱天数 Top 20)
        • [step 3:干旱事件总数排名 Top 20](#step 3:干旱事件总数排名 Top 20)
        • [step 4:最长连续干旱天数分布](#step 4:最长连续干旱天数分布)
    • [5. A4 阶段全部物化表](#5. A4 阶段全部物化表)
    • [6. 问题与排查记录](#6. 问题与排查记录)
    • [7. 阶段总结](#7. 阶段总结)

目录

前言

本文是"农业气象干旱分析"项目的第四阶段,记录在 Hive 中基于 A3 构建的数仓基础,对全部 102,430 个站点的 9,218,700 条气象观测数据进行干旱事件识别与多维统计分析的完整过程。

  • 核心内容包括:
    (1)使用窗口函数 + gaps-and-islands 算法识别连续干旱事件;
    (2)在磁盘空间濒临耗尽的极端工况下,通过 LVM 逻辑卷扩容从根本上解决 Map 端 Spill 失败问题;
    (3)通过左连接 + COALESCE 补零实现稀疏数据的全站点干旱画像;
    (4)从作业日志中反推 Hadoop 慢启动机制的实际行为,揭示了 Shuffle 阶段 Copy 子阶段可与 Map 并行执行这一教材未覆盖的工程细节。
  • 实验最终形成了一张包含全部站点干旱指标的全集画像表,并得出结论:该数据集覆盖的时空范围内,干旱是稀有且短促的现象,仅 3.75% 的站点出现过连续低降水事件。

0. 背景知识:从单站点分析到全站点画像

A3 阶段我们完成了数据概览、站点维度、时间维度分析,并选取了一个最干旱的站点进行单站点的连续低降水天数计算,结果为最长 1 天。

A3 留下了一个待解决问题:这个结论是否具有普遍性? 102,430 个站点中,有多少站点存在干旱事件?最严重的干旱持续了多久?干旱事件在空间和时间上是如何分布的?

A4 的任务就是将 A3 的单站点分析方法,系统化地推广到全部 102,430 个站点 ,形成完整的站点级干旱画像。在技术实现上,这需要解决一个经典的数据处理问题------如何从连续的时间序列中识别出每一次独立的"事件"(即连续干旱日构成的干旱事件)。SQL 领域将这类问题称为 gaps-and-islands(间隙与岛屿) 问题:将符合条件(降水 < 阈值)的连续日期合并为一个"岛屿"(干旱事件),并在不同站点间正确地划分"间隙"(非干旱期)。


1. 任务概述与目标

项目 说明
定位 承接 A3 的数仓基础与单站点分析思路,使用 Hive 窗口函数对全量站点进行干旱事件识别与多维统计
目标 1. 以 precip < 0.5mm 为干旱阈值,标记全部 921 万行数据 2. 使用 gaps-and-islands 算法识别每次独立的干旱事件(连续干旱日分组) 3. 计算站点级干旱指标:事件数、最长连续天数、平均持续天数 4. 通过左连接 + COALESCE 补零实现全站点画像 5. 输出干旱严重程度排名与分布统计
输入 A3 物化表 drought_cleaned(9,218,700 行,22 列)
输出 全站点干旱画像表 station_drought_profile(102,430 行)+ 排名与分布分析结论

2. 磁盘危机:从 Spill 失败到 LVM 扩容(工程实战)

  • 【叠甲1】此处记录了 A4 实验中最严重的一次基础设施故障------Map 端 Spill 失败,以及从排查到根治的完整过程。这是大数据平台运维中极具代表性的场景:磁盘空间看似充裕,实则被分区规划不当所困。
  • 【叠甲2】如果你觉得这些内容不值得你学习,可以直接跳到扩容操作环节,在对应虚拟机(VM)上执行命令,确保生效即可。

2.1 资源基础:可供 Hive 消耗的内存上限

2.1 故障现象

在后文步骤三中,我们执行了一条 CTAS(CREATE TABLE AS SELECT,即"建表并同时从查询结果插入数据") 语句,试图创建干旱事件表 station_dry_events

sql 复制代码
CREATE TABLE station_dry_events AS
WITH grouped AS (...)
SELECT ...

这条语句需要 Hive 启动 MapReduce 作业,对 dry_flag 表(约 293 MB)按站点进行窗口函数计算。作业提交后,反复崩溃,关键错误信息如下:

bash 复制代码
Caused by: org.apache.hadoop.util.DiskChecker$DiskErrorException: 
Could not find any valid local directory for 
attempt_1780233593964_0008_m_000000_1003_spill_0.out 
with requested size 39461383 as the max capacity in any directory is 9408512

这段日志的含义是:Map 任务在处理数据时,需要将内存中预聚合的临时数据溢写(Spill)到本地磁盘,申请写入约 39 MB 的文件,但 YARN 管理的本地临时目录中,最大的可用空间只有 约 9 MB,因此溢写失败,整个作业崩溃。

对于刚接触大数据的同学来说,CTAS 是 Hive 中非常常用的操作,CTAS一条语句可同时完成"建表"和"插入数据",避免了先 CREATE TABLEINSERT INTO 的繁琐步骤。但在执行 CTAS 时,底层仍然要启动 MapReduce 作业,因此也会遭遇所有 MR 作业可能遇到的资源问题。这次 Spill 失败就是典型的"磁盘空间看似充足,实则被非数据文件占满"的运维故障。

2.2 初步排查:HDFS 报告显示空间充裕

bash 复制代码
$ hdfs dfsadmin -report
节点 DFS 已用 DFS 剩余
worker1 3.98 GB 305 MB
worker2 3.98 GB 321 MB
worker3 3.98 GB 934 MB

表面看每个节点有 300-900 MB 可用,且 NodeManager 状态均为 RUNNING。但为何 39 MB 的 Spill 写不进去?

2.3 深入定位:根分区只有 14 GB

bash 复制代码
$ df -h /
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv   14G   13G  304M  98% /
  • 真相 :VM 分配了 30 GB 磁盘,但 Ubuntu 安装时 LVM 只给根分区划了 14 GB。Hadoop 软件(/usr/local/hadoop,约 5.4 GB)、Hive(/usr/local/hive,约 400 MB)、Java、系统文件(/usr,约 8.9 GB)、HDFS 数据(4 GB)全部挤在这 14 GB 里,剩余空间仅 304 MB。

这里有一个容易混淆的关键认知:hdfs dfsadmin -report 显示的"DFS 剩余"与 df -h 显示的"磁盘可用"是两个不同的概念

命令 / 指标 含义 我们的数值
hdfs dfsadmin -report HDFS 数据目录内还可存储 HDFS 块的空间 305 MB
df -h / 根分区剩余的全部可用空间(含非 HDFS 文件) 304 MB

两者在同一个根分区上争夺空间。HDFS 报告的"剩余 305 MB"指的是 HDFS 数据块还能用多少,而 Map 端 Spill 写到的是 YARN 本地目录yarn.nodemanager.local-dirs,本次部署中未显式配置,使用默认路径,在根分区上)。39 MB 的 Spill 请求失败,因为根分区剩余空间连这个临时文件都放不下。

2.4 无效的清理尝试

依次尝试了以下清理手段,但收效甚微:

操作 释放空间 原因
sudo apt-get clean ~0 MB APT 缓存本就不大
sudo journalctl --vacuum-size=50M 160 MB 系统日志有一定积累
sudo snap remove lxd 609 MB(未立即释放) LXD 移除后有快照残留
清理 /tmp/* 0 MB /tmp 本就是空的

清理后根分区可用空间从 304 MB 微增到 354 MB,仅释放了 50 MB------**因为 14 GB 的天花板摆在那儿,清理只是杯水车薪。**所以还是要从根上解决问题,而不是选择规避问题,毕竟咱们在A0实验中是踏踏实实给VM分配了30GB的磁盘空间呢!怎么就没了?想想这事就邪乎。

2.5 根本原因定位:LVM 闲置了 14 GB

bash 复制代码
$ sudo pvdisplay | grep -E "PV Name|PV Size"
  PV Name               /dev/sda3
  PV Size               <28.00 GiB

$ sudo vgdisplay | grep -E "VG Name|Free"
  VG Name               ubuntu-vg
  Free  PE / Size       3584 / 14.00 GiB

物理卷总大小 28 GB,卷组空闲 14 GB------一半的磁盘空间在 LVM 里闲置着。 根分区只用了其中的 14 GB。

LVM(Logical Volume Manager,逻辑卷管理器)是现代 Linux 发行版默认使用的磁盘管理方案。它在物理磁盘之上抽象出"物理卷→卷组→逻辑卷"三层结构,允许管理员动态调整分区大小。本次 Ubuntu 安装时,安装程序创建了一个 28 GB 的卷组,却只为根分区(逻辑卷)分配了 14 GB,剩余 14 GB 留在了卷组里从未使用;还是工程经验不足呢!

2.6 根本解决:LVM 在线扩容

不需要重启、不影响运行中的服务,两条命令将根分区从 14 GB 扩展到 28 GB:

bash 复制代码
# 扩展逻辑卷,吃掉全部空闲空间
sudo lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv

# 在线扩展 ext4 文件系统
sudo resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

扩容结果:

节点 扩容前 扩容后 可用空间变化
worker1 14 GB 28 GB 304 MB → 14 GB
worker2 14 GB 28 GB 321 MB → 14 GB
worker3 14 GB 28 GB 934 MB → 15 GB

此后所有 MR 作业稳定运行,不再出现 Spill 失败。

【追问】master和standby也需要检查吗?

.7 经验教训

  1. hdfs dfsadmin -report 只反映 HDFS 数据目录的空间,不代表整块磁盘 。YARN 本地目录、系统日志、软件包等"非 HDFS 文件"(Non DFS Used)同样消耗磁盘。运维中应同时使用 df -h 检查分区级别的实际可用空间。
  2. 虚拟机模板的 LVM 配置可能只用了部分磁盘pvdisplay / vgdisplay 应该在集群部署时就检查,确认逻辑卷已分配全部卷组空间,避免后续业务中断。本次排查路径------从 hdfs dfsadmin -reportdf -hdu -sh /*pvdisplayvgdisplay------层层递进,是定位磁盘类问题的通用方法。
  3. 清理只是止痛,扩容才是根治 。当分区规划本身有问题时,删文件解决不了天花板问题。LVM 在线扩容(lvextend + resize2fs)是 Linux 运维的基本功,整个过程无需重启服务,不影响正在运行的 DataNode 和 NodeManager。

3. 环境与资源背景

项目 配置
节点数 3(master + worker1/2/3)
每节点内存 2 GB RAM
每节点磁盘 30 GB → 根分区扩容后 28 GB 可用
YARN 可用内存 每节点 1024 MB,单容器最大 384 MB
Hive 版本 3.1.3
元数据库 Derby(内嵌)
执行引擎 MapReduce
数据源 drought_cleaned(9,218,700 行)
干旱阈值 precip < 0.5 mm

4. 分析查询与结果

4.1 会话参数设置

每次进入 Hive CLI 后首先执行:

sql 复制代码
SET hive.execution.engine=mr;
SET mapreduce.map.memory.mb=384;
SET mapreduce.map.java.opts=-Xmx256m;
SET mapreduce.reduce.memory.mb=384;
SET mapreduce.reduce.java.opts=-Xmx256m;
SET mapreduce.job.reduces=3;
SET hive.auto.convert.join=false;

4.2 步骤一:站点干旱日统计

sql 复制代码
CREATE TABLE station_dry_days AS
SELECT 
    station_id,
    COUNT(*) AS dry_day_count,
    ROUND(COUNT(*) / 90.0 * 100, 2) AS dry_day_pct
FROM drought_cleaned
WHERE precip < 0.5
GROUP BY station_id;
指标 数值
HDFS 写入 105,987 字节(约 103 KB)
耗时 55.6 秒

写入大小异常分析 :预期约 2-3 MB(102,430 站点 × 30 字节/行),实际仅 103 KB。原因是绝大多数站点在 WHERE precip < 0.5 过滤后无数据,不出现在 GROUP BY 结果中。后续验证证实了这一点:

sql 复制代码
SELECT 
    COUNT(*)                    AS stations_with_drought,
    MIN(dry_day_count)          AS min_dry_days,
    MAX(dry_day_count)          AS max_dry_days,
    ROUND(AVG(dry_day_count),1) AS avg_dry_days
FROM station_dry_days;
指标 数值
有干旱日的站点数 3,845(仅占总站点 3.75%)
最少干旱天数 1
最多干旱天数 27
平均干旱天数 3.0

发现 :96.25% 的站点在观测期内从未出现过 precip < 0.5 的日期。即使是有干旱日的 3,845 个站点,平均也仅 3 天。干旱在数据集中是稀有现象。

4.3 步骤二:干旱标记表(全量)

在进入 gaps-and-islands 逻辑之前,先物化全量干旱标记表,避免后续 CTE(Common Table Expression,公共表表达式,即 WITH ... AS 定义的临时查询块)被 Hive 内联展开(Inlining),导致 drought_cleaned 被重复扫描两次(一次 ROW_NUMBER(),一次 SUM() OVER)。先物化为独立的物理表,后续窗口函数直接从物化表读取,避免重复扫描全表:

sql 复制代码
CREATE TABLE dry_flag AS
SELECT 
    station_id,
    record_date,
    CASE WHEN precip < 0.5 THEN 1 ELSE 0 END AS is_dry
FROM drought_cleaned;
指标 数值
作业类型 Map-only(无 Reduce)
HDFS 写入 307,735,345 字节(约 293 MB)
行数 9,218,700(与原表一致)
耗时 34.8 秒

4.4 步骤三:干旱事件识别(gaps-and-islands)

这是 A4 最核心的 SQL,使用窗口函数为每个站点的连续干旱日生成分组标识:

sql 复制代码
CREATE TABLE station_dry_events AS
WITH grouped AS (
    SELECT 
        station_id,
        record_date,
        is_dry,
        ROW_NUMBER() OVER (PARTITION BY station_id ORDER BY record_date) AS rn,
        SUM(CASE WHEN is_dry = 0 THEN 1 ELSE 0 END) 
            OVER (PARTITION BY station_id ORDER BY record_date) AS grp
    FROM dry_flag
)
SELECT 
    station_id,
    grp,
    COUNT(*) AS event_days,
    MIN(record_date) AS event_start,
    MAX(record_date) AS event_end
FROM grouped
WHERE is_dry = 1
GROUP BY station_id, grp;
指标 数值
作业数 2 个 Stage
Stage-1 窗口函数计算,Map: 2, Reduce: 3
Stage-2 GROUP BY 聚合,Map: 1, Reduce: 3
总耗时 163.3 秒
HDFS 写入 182,648 字节(约 178 KB)

gaps-and-islands 算法逻辑解释

  1. ROW_NUMBER() OVER (PARTITION BY station_id ORDER BY record_date):为每个站点的每行分配一个连续的行号 rn,日期越早行号越小。
  2. SUM(CASE WHEN is_dry = 0 THEN 1 ELSE 0 END) OVER (PARTITION BY station_id ORDER BY record_date):累加非干旱日的数量,记为 grp。关键在于------连续干旱日之间没有非干旱日 ,所以它们共享同一个累积值;一旦遇到一个非干旱日,累积值增加 1,后续干旱日的 grp 就与上一段干旱不同了。
  3. 最终每个站点内,相同的 grp 值对应一次独立的干旱事件(一个"岛屿"),grp 的跳变处就是事件之间的"间隙"。
执行机制深度分析:为什么两个 Stage 都有 Map 和 Reduce?

Stage-1 执行窗口函数(ROW_NUMBER() OVERSUM() OVER),是一个完整的 MapReduce:

  • Map 端 :读取 dry_flag 的 293 MB 数据,按 station_id 做 Partition,按 record_date 排序
  • Reduce 端:每个分区内逐行计算行号和累加值

Stage-2 执行外层 GROUP BY station_id, grp,是另一个完整 MapReduce:

  • Map 端 :读取 Stage-1 输出,以 (station_id, grp) 为 Key
  • Reduce 端:对每个 Key 做 COUNT、MIN、MAX 聚合

不能合并的原因 :窗口函数和 GROUP BY 是两种不同的 Reduce 操作------前者需要按 station_id 分区排序,后者需要按 (station_id, grp) 分组聚合。Hive 优化器无法将它们合并为一个 Reduce 任务,必须串行执行两个 MR 作业。

慢启动机制:Map 未到 100%,Reduce 为何已有进度?

日志中观察到一个与教材理论相悖的现象:

复制代码
Stage-1 map = 84%,  reduce = 6%
Stage-1 map = 84%,  reduce = 11%
Stage-1 map = 94%,  reduce = 17%

教材说法:所有 Map 完成后,Reduce 才开始。

实际行为:Map 还差 16%,Reduce 已经推进到 17%。

这不是错误,而是 Hadoop 的 慢启动(Slow Start)机制 ,由参数 mapreduce.job.reduce.slowstart.completedmaps 控制,默认值 0.05------当 5% 的 Map 任务完成后,Reduce 即开始启动。本作业有 2 个 Map 任务,5% × 2 = 0.1,意味着第一个 Map 一完成(即 50% 进度),Reduce 就开始预热了。

慢启动的合理性在于 Reduce 端的三个阶段:

阶段 做什么 能否提前?
Copy(拉取) 从各 Map 节点拉取输出文件 ✅ 能------某个 Map 一完成,其输出即可被拉走
Sort(排序) 合并排序所有拉取的数据 ❌ 不能------必须等全部到齐
Reduce(聚合) 逐条计算 ❌ 不能------排序没完成没法算

日志中 reduce 进度从 6% 缓慢爬升到 17%,走的是 Copy 阶段------在拉取已完成 Map 的数据,与剩余 Map 完全并行。Copy 只是网络搬运,不涉及计算逻辑,因此不依赖全量数据。Reduce 进度的前三分之一(0%-33%)通常对应 Copy 阶段,33%-66% 对应 Sort 阶段,66%-100% 对应真正的 Reduce 计算。

教材为什么简化? 教材讲的是 Reduce 的"计算阶段"(Sort + Reduce)必须等所有 Map 完成,但通常不细分 Shuffle 内部的三个子阶段。这个细节揭示了 逻辑阶段 ≠ 物理执行 的工程真相。

慢启动机制是 Hadoop 从 0.x 时代就具备的生产级优化,并非 3.0 才引入。

4.5 步骤四:事件级统计

sql 复制代码
CREATE TABLE station_dry_summary AS
SELECT 
    station_id,
    COUNT(*) AS dry_event_count,
    MAX(event_days) AS max_dry_days,
    ROUND(AVG(event_days), 1) AS avg_dry_days,
    SUM(event_days) AS total_dry_days
FROM station_dry_events
GROUP BY station_id;
指标 数值
耗时 30.8 秒
HDFS 写入 117,696 字节(约 115 KB)

4.6 步骤五:全站点干旱画像(稀疏补零)

station_dry_summary 只包含有干旱事件的 ~3,845 个站点。为形成完整的全站点画像,需将无干旱站点补零。在数据分析领域,这一操作有明确术语和理论依据:

术语:稀疏数据补全(Sparse Data Completion)/ 零填充(Zero-Filling),属于缺失值填充(Missing Value Imputation)的一种------用领域合理的常数(0 表示"没有干旱事件")填补外连接产生的 NULL。

理论依据

  • 语义正确性LEFT JOIN 产生 NULL 的原因不是"数据未知",而是"该站点确定没有发生过干旱事件"。干旱事件次数为 0 是确定性事实,补零是还原真相,而非捏造数据。这区别于传感器故障等造成的"真正未知"的缺失值。
  • 关系代数语义 :在标准 SQL 中,A LEFT JOIN B 中未匹配行的 B 列用 NULL 填充,这是 SQL 规范定义的占位行为。COALESCE(col, 0) 是在此基础上做语义修正------将 SQL 默认占位符替换为符合业务含义的真实值。
  • 数据集完整性:补零后分布一目了然(0 占 96.25%,1 占 1.61%...),是完整的数据描述。不补零则只能描述 3.75% 的站点,无法回答"全量站点中干旱有多普遍"这一问题。
  • 防止 NULL 传播 :SQL 的 NULL 具有传染性(NULL + 1 = NULLAVG(col_with_null) 会跳过 NULL 行),补零消除了后续计算的隐患,使 station_drought_profile 可以被任意下游查询安全使用。
sql 复制代码
CREATE TABLE station_drought_profile AS
SELECT 
    a.station_id,
    COALESCE(s.dry_event_count, 0) AS dry_event_count,
    COALESCE(s.max_dry_days, 0)   AS max_dry_days,
    COALESCE(s.avg_dry_days, 0.0)  AS avg_dry_days,
    COALESCE(s.total_dry_days, 0)  AS total_dry_days
FROM (
    SELECT DISTINCT station_id FROM dry_flag
) a
LEFT JOIN station_dry_summary s
ON a.station_id = s.station_id;
指标 数值
作业数 2 个 Stage
Stage-1 SELECT DISTINCT,Map: 2, Reduce: 3
Stage-2 LEFT JOIN + COALESCE,Map: 2, Reduce: 3
总耗时 78.0 秒
HDFS 写入 3,112,886 字节(约 3.0 MB)
行数 102,430(全量站点)

4.7 步骤六:排名与分布分析

step 1:总体概览
sql 复制代码
SELECT 
    COUNT(*)                              AS total_stations,
    SUM(CASE WHEN dry_event_count > 0 THEN 1 ELSE 0 END) AS stations_with_drought,
    ROUND(SUM(CASE WHEN dry_event_count > 0 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS drought_pct,
    MAX(max_dry_days)                     AS overall_max_dry_days,
    ROUND(AVG(total_dry_days), 1)         AS overall_avg_dry_days
FROM station_drought_profile;
指标 数值
全站点数 102,430
有干旱事件站点数 3,845(3.75%)
无干旱事件站点数 98,585(96.25%)
全站点最长连续干旱天数 27 天
全站点平均干旱总天数 0.1 天
step 2:最长连续干旱天数 Top 20
sql 复制代码
SELECT station_id, dry_event_count, max_dry_days, avg_dry_days
FROM station_drought_profile
ORDER BY max_dry_days DESC
LIMIT 20;
排名 站点 ID 干旱事件数 最长连续天数 平均持续天数
1 6850186147108203597 1 27 27.0
2 8684056272254568774 1 25 25.0
3 -8915429204746688844 1 24 24.0
4-5 (2 个站点) 1 23 23.0
6-7 (2 个站点) 1 22 22.0
8-9 (2 个站点) 1 21 21.0
10-12 (3 个站点) 1 20 20.0
... ... ... ... ...

特征 :所有 Top 20 站点的干旱事件数均为 1。它们只经历了一次干旱事件,但此次事件持续了 18-27 天,是真正的"干旱站点"。

step 3:干旱事件总数排名 Top 20
sql 复制代码
SELECT station_id, dry_event_count, max_dry_days, avg_dry_days
FROM station_drought_profile
ORDER BY dry_event_count DESC
LIMIT 20;

所有 Top 20 站点的 dry_event_count 均为 1 。这表明:整个数据集中不存在经历多次独立干旱事件的站点。干旱一旦发生,就是单次、连续的。

:Hive 在分布式排序中,ORDER BY dry_event_count DESC LIMIT 20 因大量并列第一(均为 1 次),返回的站点 ID 分布在数据分片中带有随机性。这不影响结论的统计有效性,但可在后续分析中加 ORDER BY dry_event_count DESC, station_id 保证确定性排序。

step 4:最长连续干旱天数分布
sql 复制代码
SELECT 
    max_dry_days,
    COUNT(*) AS station_cnt,
    ROUND(COUNT(*) / 102430.0 * 100, 2) AS pct
FROM station_drought_profile
GROUP BY max_dry_days
ORDER BY max_dry_days DESC;
最长连续天数 站点数 占比
0 98,585 96.25%
1 1,649 1.61%
2 751 0.73%
3 398 0.39%
4 268 0.26%
5 222 0.22%
6 148 0.14%
7 103 0.10%
8 61 0.06%
9 40 0.04%
10 45 0.04%
11 31 0.03%
12 28 0.03%
13 25 0.02%
14 17 0.02%
15 13 0.01%
16 10 0.01%
17 13 0.01%
18 9 0.01%
19 2 0.00%
20 3 0.00%
21 2 0.00%
22 2 0.00%
23 2 0.00%
24 1 0.00%
25 1 0.00%
27 1 0.00%

分布特征:严重右偏/长尾分布。绝大多数站点集中在 0 天,随着天数增加,站点数急剧下降。只有 1 个站点达到 27 天连续低降水。

【追问】咱们做了那么久数据分析,你是否想过这些分析结果其实是有可视化价值的呢?如果你认为有,可以做出漂亮、表达力强的可视化效果吗?若你认为不必做可视化,也请给出令人信服的说法。


5. A4 阶段全部物化表

表名 行数 HDFS 大小 用途
station_dry_days 3,845 ~103 KB 各站点干旱日总数
dry_flag 9,218,700 ~293 MB 全量干旱标记(is_dry 列)
station_dry_events ~3,845+ ~178 KB 每次干旱事件的起止日期与持续天数
station_dry_summary 3,845 ~115 KB 站点级干旱事件统计
station_drought_profile 102,430 ~3.0 MB 全站点干旱画像(补零后,最终表)

6. 问题与排查记录

现象 原因 解决 备注
CTAS 报 Table already exists Hive 的 CREATE TABLE ... AS 不覆盖已有表 DROP TABLE IF EXISTS ... 后重跑 保护机制,防止误覆盖
Map 端 Spill 失败:Could not find any valid local directory 根分区仅 14 GB,Hadoop + 系统占满后剩 304 MB,39 MB 的 Spill 写不进去 LVM 在线扩容:lvextend -l +100%FREE + resize2fs,根分区 14→28 GB 本次最严重的故障,根本原因是 VM 模板 LVM 配置只用了一半磁盘
磁盘清理无效(清理后仅释放 50 MB) 14 GB 天花板摆在那儿,清理治标不治本 扩容根治 教训:部署时就该检查 pvdisplay / vgdisplay
扩容后 df -h 未立即显示空间释放 resize2fs 执行后才生效 两个命令都已正确执行,最终 28G 确认有效
dry_flag CTAS 写入 293 MB 但源表 1.29 GB 只选了 3 列(station_id, record_date, is_dry),而源表 22 列 正常,列裁剪减少数据量 Map-only 作业,高效
station_dry_events CTAS 触发 2 个 Stage 窗口函数(Stage-1)+ GROUP BY(Stage-2)无法合并为一个 Reduce 正常行为,Hive 优化器不做此类合并 总耗时 163 秒,内存安全
Map 未 100% 时 Reduce 进度已到 17% Hadoop 慢启动机制(slowstart.completedmaps=0.05),Copy 阶段可提前 正常行为 教材理论简化所致,详见 4.4 节分析
ORDER BY dry_event_count DESC LIMIT 20 返回的站点 ID 带有随机性 所有站点干旱事件数均为 1(并列第一),分布式排序下 LIMIT 返回不同分片中的行 正常行为,不影响结论 可加 station_id 做二级排序保证确定性

7. 阶段总结

  • 磁盘危机与根治 :本次实验最大的挑战来自基础设施------根分区仅 14 GB 的空间被 Hadoop 全家桶、Hive、Java、系统文件和 HDFS 数据塞满,Map 端 Spill 39 MB 临时文件失败。经过从 hdfs dfsadmin -reportdf -hdu -sh /*pvdisplayvgdisplay 的逐层排查,定位到 LVM 闲置 14 GB 的根本原因,通过在线扩容将根分区从 14 GB 扩展到 28 GB,彻底解决磁盘瓶颈。这一过程真实还原了生产环境中"报告显示有空间,实际写不进去"的排查路径,也印证了 hdfs dfsadmin -reportdf -h 两个命令关注不同层面的磁盘使用情况的原理性区别。

  • 干旱事件识别 :使用 gaps-and-islands 算法(ROW_NUMBER + SUM(CASE ...) OVER 差值分组)成功识别出全部站点的干旱事件。SQL 翻译为两个 MR Stage,窗口函数按 station_id 分区(每个分区仅 90 行),窗口缓冲极小,内存始终安全。先物化 dry_flag 避免了 CTE 内联导致的全表重复扫描,是低资源环境下的重要优化技巧。

  • 慢启动机制发现:从作业日志中观察到 Map 进度 84% 时 Reduce 已有 6%-17% 进度的现象,追溯出 Hadoop 慢启动机制的工程实现------Reduce 的 Copy 阶段可在 Map 未全部完成时提前拉取数据,与教材"Map 完成后 Reduce 才开始"的简化描述形成对照。这一发现体现了对 MapReduce 执行模型三阶段(Copy-Sort-Reduce)的深入理解。

  • 稀疏数据补零:针对 96.25% 站点无干旱事件的稀疏聚合结果,采用全站点左连接 + COALESCE 补零策略,将稀疏表补全为稠密的全站点干旱画像表。补零的理论依据在于"无干旱事件"是确定性事实而非数据缺失,0 是其统计量的真实值。这一操作在数据分析领域对应"稀疏数据补全"和"缺失值填充(Zero-Filling)"。

  • 分析结论:数据覆盖的 102,430 个站点、105 个月中,96.25% 的站点从未出现过连续低降水(< 0.5mm)。即使是 3,845 个有干旱的站点,平均干旱总天数仅 3 天。Top 20 最干旱站点均只经历 1 次干旱事件,最长持续 27 天。干旱在该数据集中是稀有、短促的现象。这一结论为后续 A5-A7 的干旱等级划分、极端事件识别提供了明确的基线------分析重心应从"寻找长期连续干旱"转向"识别间歇性低降水异常"。

  • 物化表体系 :A4 新增 5 张物化表,其中 station_drought_profile(102,430 行全站点画像)是核心输出,可直接供后续实验(A5-A15)复用,避免重复全表扫描。

相关推荐
RingWu2 小时前
高并发三板斧-异步
分布式·微服务·架构
冰上浮云2 小时前
Gravitino iceberg catalog backend 为hive 获取元数据过程
数据仓库·hive·hadoop·gravitino
段一凡-华北理工大学3 小时前
工业领域的Hadoop架构学习~系列文章06:Hive数据仓库
数据仓库·hadoop·架构·高炉炼铁·工业智能体·高炉智能化·hive数据仓库
搞科研的小刘选手12 小时前
【中山大学主办】第六届计算机科学与区块链国际学术会议(CCSB 2026)
分布式·神经网络·计算机视觉·区块链·计算机科学·共识算法·自然语言
小饼干在学嘎瓦13 小时前
本地缓存和分布式缓存如何选择?
分布式·缓存
XLYcmy15 小时前
全链路验证测试系统:一个针对智能代理(Agent)系统全链路能力的自动化验证脚本
分布式·python·http·网络安全·ai·llm·agent
zgl_2005377917 小时前
源代码:跨数据库通用SQL语法解析与标注拆解
大数据·数据库·数据仓库·sql·etl·源代码管理
暴躁小师兄数据学院18 小时前
【AI大数据工程师特训笔记】第13讲:数据库性能手术刀
大数据·数据库·数据仓库·sql·postgresql
phltxy1 天前
HAProxy安装与RabbitMQ负载均衡配置
分布式·rabbitmq·负载均衡