快速实验篇(A2-1)基于MapReduce的数据质量筛查与清洗

小肥柴的Hadoop之旅 快速实验篇(A2-1)基于MapReduce的数据质量筛查与清洗

    • 目录
    • [0. 概要](#0. 概要)
    • [1. 准备工作](#1. 准备工作)
      • [1.1 前置条件与输入](#1.1 前置条件与输入)
      • [1.2 集群环境验证(关键前置步骤)](#1.2 集群环境验证(关键前置步骤))
        • [1.2.1 YARN 节点注册检查](#1.2.1 YARN 节点注册检查)
        • [1.2.2 完整调度链路测试](#1.2.2 完整调度链路测试)
    • [2 虚拟机内存适配与集群配置](#2 虚拟机内存适配与集群配置)
      • [2.1 Worker节点内存压力分析](#2.1 Worker节点内存压力分析)
      • [2.2 集群全局配置](#2.2 集群全局配置)
        • [2.2.1 更新配置文件内容](#2.2.1 更新配置文件内容)
        • [2.2.2 同步配置](#2.2.2 同步配置)
      • [2.3 作业资源适配分析](#2.3 作业资源适配分析)
    • [3. 数据质量校验规则](#3. 数据质量校验规则)
    • [4. 代码实现](#4. 代码实现)
      • [4.1 选择MultipleOutputs的理由](#4.1 选择MultipleOutputs的理由)
      • [4.2 Mapper关键API:setup / map / cleanup](#4.2 Mapper关键API:setup / map / cleanup)
      • [4.3 完整代码](#4.3 完整代码)
    • [5 本地开发环境配置(Windows + IDEA + Maven)](#5 本地开发环境配置(Windows + IDEA + Maven))
      • [5.1 安装 Eclipse Temurin JDK 8](#5.1 安装 Eclipse Temurin JDK 8)
      • [5.2 IDEA 配置](#5.2 IDEA 配置)
      • [5.3 项目结构与 pom.xml](#5.3 项目结构与 pom.xml)
      • [5.4 编译打包](#5.4 编译打包)
    • [6. 作业提交](#6. 作业提交)
      • [6.1 命令说明](#6.1 命令说明)
      • [6.2 运行日志关键节点说明](#6.2 运行日志关键节点说明)
    • [7. 首次运行结果与数据质量探查](#7. 首次运行结果与数据质量探查)
      • [7.1 输出文件结构](#7.1 输出文件结构)
      • [7.2 行数统计(单个 Map 输出文件示例)](#7.2 行数统计(单个 Map 输出文件示例))
    • [8. 在实验过程中常见问题与排查记录](#8. 在实验过程中常见问题与排查记录)
    • [9. 实验A2-1总结](#9. 实验A2-1总结)
    • [与 A2-2 的衔接和任务预告](#与 A2-2 的衔接和任务预告)

目录

0. 概要

项目 说明
定位 农业气象干旱分析模块的第2个任务,首次使用MapReduce进行批量数据处理
目标 1. 应用MapReduce对CSV数据执行七项质量校验 2. 将合规记录与问题记录(含原因)分流输出 3. 在2GB内存受限环境中完成集群配置与调优 4. 完成首次清洗运行,进行数据质量探查与问题定位
输入 HDFS路径 /drought/raw/test_set.csv(含表头,约1.07GB,22个字段)
输出 /drought/cleaned:通过全部校验的记录 /drought/rejected:未通过校验的记录及异常标签

1. 准备工作

1.1 前置条件与输入

  • 集群状态:Hadoop 完全分布式集群已搭建,HDFS 和 YARN 服务已启动
  • 基础数据 :HDFS 中存在 /drought/raw/test_set.csv(由上一阶段生成,约 10 个 Block)
  • 开发工具:Windows 环境下的 IntelliJ IDEA + Maven,需额外配置 JDK 8(即使系统默认 JDK 为更高版本);或直接在集群节点上用 Vim + 命令行编译
  • 物理机环境:内存 16GB,运行 5 台虚拟机(每台 2GB)后总内存占用约 10GB,建议关闭浏览器等非必要应用

1.2 集群环境验证(关键前置步骤)

jps 命令仅能确认 Java 进程存在,无法验证服务间的通信和资源就绪情况。以下检查必须在作业提交前全部通过,确保所有节点通讯正常、可用。

1.2.1 YARN 节点注册检查
bash 复制代码
yarn node -list

(1)预期输出 :3 个节点,状态均为 RUNNING

(2)异常情况 Total Nodes:0 :检查 yarn-site.xmlyarn.resourcemanager.hostname 是否正确配置。

【注】也可以使用ARN Web UI 佐证,但也仅仅是佐证:

访问 http://192.168.10.101:8088 ,查看 Nodes 标签,确认每个 Worker 的 Memory 显示为 1024 MB(或者以上,我的是1.5GB)。

1.2.2 完整调度链路测试

尝试运行官方提供的案例程序,检测调度功能是否正常。

bash 复制代码
hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar pi 2 2

(1)预期输出 :作业状态为 FINISHEDSUCCEEDED

(2)若失败,常见原因

  • MRAppMaster 找不到,检查: mapred-site.xml 是否缺失 yarn.app.mapreduce.am.env配置。
  • mapreduce_shuffle 服务不存在,检查: yarn-site.xml 是否缺失 yarn.nodemanager.aux-services配置。
  • 容器内存超限,检查:yarn.scheduler.maximum-allocation-mb配置。

2 虚拟机内存适配与集群配置

各节点仅2GB物理内存,必须精细裁剪YARN与MapReduce参数,否则作业无法获取任何Container;因此需要调整配置参数。

2.1 Worker节点内存压力分析

组件 预估占用 说明
Ubantu 最小化安装 300--400 MB 无GUI
DataNode进程 512 MB HADOOP_DATANODE_HEAPSIZE=512
NodeManager进程 512 MB YARN_NODEMANAGER_HEAPSIZE=512
系统缓存及其他 200--300 MB 文件系统缓存等
剩余给YARN Container 约300--400 MB 必须将Container内存限定在此范围

默认的 yarn.scheduler.minimum-allocation-mb=1024 会直接导致资源无法分配,需要调整。

2.2 集群全局配置

2.2.1 更新配置文件内容

以下配置在所有节点的相应XML文件中生效,建议先在master节点更新文件,后同步到其他节点。

(1)yarn-site.xml

bash 复制代码
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>

    <property>
        <name>dfs.namenode.name.dir</name>
        <value>/usr/local/hadoop/data/namenode</value>
    </property>

    <property>
        <name>dfs.datanode.data.dir</name>
        <value>/usr/local/hadoop/data/datanode</value>
    </property>

    <property>
        <name>dfs.replication</name>
        <value>3</value>
    </property>

    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>standby:9868</value>
    </property>

    <property>
        <name>dfs.datanode.max.transfer.threads</name>
        <value>256</value>
    </property>

    <property>
        <name>dfs.datanode.handler.count</name>
        <value>3</value>
    </property>

</configuration>

(2)mapred-site.xml

bash 复制代码
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>

    <!-- 使用 YARN 作为 MapReduce 框架 -->
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>

    <!-- ApplicationMaster 内存 -->
    <property>
        <name>yarn.app.mapreduce.am.resource.mb</name>
        <value>384</value>
    </property>
    <property>
        <name>yarn.app.mapreduce.am.command-opts</name>
        <value>-Xmx256m</value>
    </property>

    <!-- Hadoop 环境变量(解决 MRAppMaster 找不到类) -->
    <property>
        <name>yarn.app.mapreduce.am.env</name>
        <value>HADOOP_MAPRED_HOME=/usr/local/hadoop</value>
    </property>
    <property>
        <name>mapreduce.map.env</name>
        <value>HADOOP_MAPRED_HOME=/usr/local/hadoop</value>
    </property>
    <property>
        <name>mapreduce.reduce.env</name>
        <value>HADOOP_MAPRED_HOME=/usr/local/hadoop</value>
    </property>

    <!-- Map Task 内存 -->
    <property>
        <name>mapreduce.map.memory.mb</name>
        <value>384</value>
    </property>
    <property>
        <name>mapreduce.map.java.opts</name>
        <value>-Xmx256m</value>
    </property>

    <!-- Reduce Task 内存(Map-only 作业用不到,但配置了无害) -->
    <property>
        <name>mapreduce.reduce.memory.mb</name>
        <value>384</value>
    </property>
    <property>
        <name>mapreduce.reduce.java.opts</name>
        <value>-Xmx256m</value>
    </property>

    <!-- 排序缓冲 -->
    <property>
        <name>mapreduce.task.io.sort.mb</name>
        <value>64</value>
    </property>

    <!-- 关闭推测执行 -->
    <property>
        <name>mapreduce.map.speculative</name>
        <value>false</value>
    </property>
    <property>
        <name>mapreduce.reduce.speculative</name>
        <value>false</value>
    </property>

</configuration>

(3)hdfs-site.xml(可选)

bash 复制代码
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>

    <property>
        <name>dfs.namenode.name.dir</name>
        <value>/usr/local/hadoop/data/namenode</value>
    </property>

    <property>
        <name>dfs.datanode.data.dir</name>
        <value>/usr/local/hadoop/data/datanode</value>
    </property>

    <property>
        <name>dfs.replication</name>
        <value>3</value>
    </property>

    <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>standby:9868</value>
    </property>

    <property>
        <name>dfs.datanode.max.transfer.threads</name>
        <value>256</value>
    </property>

    <property>
        <name>dfs.datanode.handler.count</name>
        <value>3</value>
    </property>

</configuration>
2.2.2 同步配置

(1)使用命令同步配置文件

bash 复制代码
# 同步所有配置
for host in worker1 worker2 worker3 standby; do
  scp $HADOOP_HOME/etc/hadoop/yarn-site.xml $host:$HADOOP_HOME/etc/hadoop/
  scp $HADOOP_HOME/etc/hadoop/mapred-site.xml $host:$HADOOP_HOME/etc/hadoop/
  scp $HADOOP_HOME/etc/hadoop/hdfs-site.xml $host:$HADOOP_HOME/etc/hadoop/
done

同步完成后,需要重启yarn:

bash 复制代码
stop-yarn.sh
start-yarn.sh

或者

bash 复制代码
stop-yarn.sh && start-yarn.sh

且重启后再次使用命令查看节点在线情况(对照"1.2.1 YARN 节点注册检查"):

bash 复制代码
yarn node -list

(2)配置文件核对要点

  • 严禁出现重复的 <?xml?> 声明或多个 <configuration> 块,否则解析器行为不可预测。
  • 修改配置使用 cat > 命令完整覆盖文件,避免 vim 追加导致重复。
  • 修改后必须同步到所有节点并重启受影响服务。

2.3 作业资源适配分析

  • 输入分片:1.07GB → 9个Block → 9个Map Task。
  • 任务特性:纯过滤,Reducer数量设为0。作业代码中必须强制指定0 Reducer:
java 复制代码
job.setNumReduceTasks(0);
  • 单Container:384MB物理内存,256MB JVM堆,处理逐行文本绰绰有余。
  • 最大并发:每 Worker 可运行 2 个 Container(2 × 384 = 768MB < 1024MB),3 个 Worker 共 6 个 Map 可并行,剩余 4 个排队分批执行。

3. 数据质量校验规则

CSV第一行为表头,从第二行开始逐行检查。采用"短路"策略,每条记录仅报告第一个命中的违规规则。

规则ID 检查项 判定逻辑 异常标签
R1 字段数量 逗号分隔后必须为22列 COLUMN_COUNT_MISMATCH
R2 站点ID 非空,仅含字母、数字、下划线 INVALID_STATION
R3 日期格式 yyyy-mm-dd且为合法日期 INVALID_DATE
R4 数值字段 第5列及之后所有气象指标可解析为数字 NON_NUMERIC_VALUE
R5 降水量范围 PRECTOT(第6列)≥ 0 PRECTOT_NEGATIVE
R6 温度范围 T2M(第9列)在 -50~50℃ 之间 T2M_OUTLIER
R7 风速范围 WS10M(第17列)在 0~100 m/s WS10M_OUTLIER

【注】

(1)R5--R7的阈值基于地理常识,可在代码中修改以适应不同的业务约束。

(2)R2 规则在本阶段使用 [A-Za-z0-9_]+,首次运行后将被发现与真实数据特征不匹配,将在 实验A2-2 阶段修正。


4. 代码实现

(Java版)基于MultipleOutputs的Map-only作业

4.1 选择MultipleOutputs的理由

需求是一次读取、两路输出(cleanedrejected)。若采用"Mapper打标签→Reducer分流"将引入不必要的Shuffle和Reduce阶段,在2GB内存下极易OOM。MultipleOutputs让作业保持Map-only,单次扫描即可将记录写入不同命名输出,无网络传输开销,且输出目录结构清晰,方便后续Hive直接消费。

4.2 Mapper关键API:setup / map / cleanup

Mapper类的三个核心方法由框架按固定顺序调用,第一次接触简单了解即可:

  • setup(Context context) :每个Map Task初始化时执行一次,适合一次性资源分配,如创建 MultipleOutputs 对象。
  • map(LongWritable key, Text value, Context context):对输入分片中的每一行调用一次,核心业务逻辑写在此处。
  • cleanup(Context context) :Map Task结束时执行一次,用于释放资源,必须在此关闭 MultipleOutputs,否则输出文件可能不完整。

4.3 完整代码

(1)DroughtClean.java (Driver,包名:com.lab.a2

【注】可以根据需要调整入参判定逻辑。

java 复制代码
package com.lab.a2;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.MultipleOutputs;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;

public class DroughtClean {

    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            System.err.println("Usage: DroughtClean <input path> <output path>");
            System.exit(-1);
        }

        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "Drought Data Cleaning");

        job.setJarByClass(DroughtClean.class);
        job.setMapperClass(CleanMapper.class);
        job.setNumReduceTasks(0);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        MultipleOutputs.addNamedOutput(job, "cleaned",
                TextOutputFormat.class, Text.class, Text.class);
        MultipleOutputs.addNamedOutput(job, "rejected",
                TextOutputFormat.class, Text.class, Text.class);

        FileInputFormat.addInputPath(job, new Path(args[0]));
        FileOutputFormat.setOutputPath(job, new Path(args[1]));

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

(2)CleanMapper.java (Mapper,包名:com.lab.a2

java 复制代码
package com.lab.a2;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.output.MultipleOutputs;

import java.io.IOException;
import java.text.SimpleDateFormat;

public class CleanMapper extends Mapper<LongWritable, Text, Text, Text> {

    private MultipleOutputs<Text, Text> mos;

    private static final int COL_STATION = 0;
    private static final int COL_DATE    = 1;
    private static final int COL_PRECTOT = 5;
    private static final int COL_T2M     = 8;
    private static final int COL_WS10M   = 16;

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    protected void setup(Context context) throws IOException, InterruptedException {
        mos = new MultipleOutputs<>(context);
        DATE_FORMAT.setLenient(false);
    }

    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {

        String line = value.toString();
        if (line.startsWith("station")) return;

        String[] fields = line.split(",");
        String reason = null;

        if (fields.length != 22) {
            reason = "COLUMN_COUNT_MISMATCH:" + fields.length;
        }
        else if (!fields[COL_STATION].matches("[A-Za-z0-9_]+")) {
            reason = "INVALID_STATION";
        }
        else if (!isValidDate(fields[COL_DATE])) {
            reason = "INVALID_DATE";
        }
        else if (!areMetricsNumeric(fields)) {
            reason = "NON_NUMERIC_VALUE";
        }
        else if (Double.parseDouble(fields[COL_PRECTOT]) < 0) {
            reason = "PRECTOT_NEGATIVE";
        }
        else if (Math.abs(Double.parseDouble(fields[COL_T2M])) > 50) {
            reason = "T2M_OUTLIER";
        }
        else if (Double.parseDouble(fields[COL_WS10M]) < 0 ||
                 Double.parseDouble(fields[COL_WS10M]) > 100) {
            reason = "WS10M_OUTLIER";
        }

        if (reason != null) {
            mos.write("rejected", new Text(line), new Text(reason));
        } else {
            mos.write("cleaned", new Text(line), new Text("OK"));
        }
    }

    @Override
    protected void cleanup(Context context) throws IOException, InterruptedException {
        mos.close();
    }

    private boolean isValidDate(String dateStr) {
        try {
            DATE_FORMAT.parse(dateStr);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private boolean areMetricsNumeric(String[] fields) {
        for (int i = 4; i < fields.length; i++) {
            try {
                Double.parseDouble(fields[i]);
            } catch (NumberFormatException e) {
                return false;
            }
        }
        return true;
    }
}

5 本地开发环境配置(Windows + IDEA + Maven)

当前Hadoop3需要适配JDK8,但很多朋友的版本是高于需求的,不适配(譬如当前系统已安装 Java 25),需要做如下调整。

5.1 安装 Eclipse Temurin JDK 8

  • 访问 Adoptium 官网,这是官方认可的JDK适配第三方。
  • 选择 Temurin 8 (LTS) ,操作系统 Windows ,架构 x64 ,下载 .msi 安装包。
  • 运行安装程序,保持默认路径。不要勾选"将 Java 添加到系统 PATH",避免覆盖系统默认 JDK。

5.2 IDEA 配置

调整编译和打包配置:

(1)File → Project Structure → Project:SDK 选择 JDK 8,Language level 设为 8

(2)File → Settings → Maven → Runner:JRE 选择 Use Project JDK

(3)File → Settings → Java Compiler:Project bytecode version 设为 8

5.3 项目结构与 pom.xml

bash 复制代码
MyHadoopA/
├── pom.xml
└── src/main/java/com/lab/a2/
    ├── DroughtClean.java
    └── CleanMapper.java

对应pom文件内容:

xml 复制代码
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.lab</groupId>
    <artifactId>drought-mr</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <hadoop.version>3.3.6</hadoop.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>${hadoop.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
            <version>${hadoop.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>${hadoop.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.lab.a2.DroughtClean</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

注意<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 不可或缺,否则含中文字符的源码会被 Maven 以 GBK 编码编译而报错。

5.4 编译打包

(1)Maven打包 :使用 IDEA 右侧 Maven 面板,依次双击 cleanpackage。生成的 jar 包位于 target/drought-mr-1.0.jar;此方式强制使用项目指定的 JDK 8。

(2)验证字节码版本 :在 IDEA 中打开 target/classes/com/lab/a2/DroughtClean.class,IDE 状态栏会显示"Java 8"。


6. 作业提交

6.1 命令说明

jar 包 pom.xml 中已配置 Main-Class命令行中不应再出现主类名 ,否则会被 Hadoop 误当作参数传入,导致 Usage 提示。

bash 复制代码
# 正确命令
hadoop jar drought-mr-1.0.jar /drought/raw/test_set.csv /drought/output_a2

也可以尝试如下命令(调整Driver中参数检测逻辑后):

powershell 复制代码
# 如需显式覆盖内存参数(全局配置已生效时无需)
hadoop jar drought-mr-1.0.jar \
  -D mapreduce.map.memory.mb=384 \
  -D mapreduce.map.java.opts=-Xmx256m \
  -D mapreduce.job.reduces=0 \
  /drought/raw/test_set.csv \
  /drought/output_a2

建议在每次执行前,先删除HDFS上已存在的输出目录:

bash 复制代码
hdfs dfs -rm -r /drought/output_a2

6.2 运行日志关键节点说明

控制台中输出信息很多,建议运行成功后(SUCCESS字样)抛给AI快速学习解读这些信息,能够学到更多知识,此处仅给出关键要点方便核对运行情况:

bash 复制代码
Submitted application application_xxx #提交作业ID
Running job: job_xxx
map 0% reduce 0%
map 10% → 25% → 50% → 74% → 90% → 100%  #运行进度
Job job_xxx completed successfully

以下关键信息仅供参考:

  • Launched map tasks: 10
  • Map input records: 9,218,701
  • Map output records: 0(因使用 MultipleOutputs,不走传统计数!)

此外请自行查看WebUI(譬如我的查询地址:http://192.168.10.101:8088),监控任务提交后的运行情况,但如果内存比较吃紧的话,谨慎操作;个人建议有条件的话,应该点开页面上的各种按钮,能学到很多新知识,特别是log的解读;我们这个教程属于短平快类,加上AI能够辅助解读,所以就不做深入讨论了,充分留白嘛。


7. 首次运行结果与数据质量探查

7.1 输出文件结构

按照之前提交作业的命令设定,查看HDFS上的输出目录

bash 复制代码
hdfs dfs -ls /drought/output_a2

实际输出目录包含三类文件:

  • _SUCCESS:作业成功标记
  • cleaned-m-00000 ~ cleaned-m-00009:清洗后的合规数据
  • rejected-m-00000 ~ rejected-m-00009:被拒数据(含拒绝原因)
  • part-m-00000 ~ part-m-00009:框架默认输出(大小为 0,符合预期)

7.2 行数统计(单个 Map 输出文件示例)

执行如下命令,可以统计两种清洗结果文件数据信息:

powershell 复制代码
hdfs dfs -cat /drought/output_a2/cleaned-m-00000 | wc -l   # 485,481
hdfs dfs -cat /drought/output_a2/rejected-m-00000 | wc -l  # 492,570

【实际观察到的现象】cleanedrejected 文件大小几乎对称,脏数据比例异常偏高。

【反思】 如何解释这个不太理想的数据清洗结果:难道这种真实的官方数据质量那么差吗?是数据本身的问题?还是咱们的清洗策略有问题?回想之前清洗程序对失效数据做了分类标记,使用如下命令查看统计情况:

bash 复制代码
hdfs dfs -cat /drought/output_a2/rejected-m-* | awk -F'\t' '{print $2}' | sort | uniq -c | sort -nr

实际输出结果:

bash 复制代码
4616190 INVALID_STATION

打开原始数据文件,观察后可以确认:数据集中站点 ID 为带负号的长整数(如 -6912263433155848174),而规则 R2 使用的正则 [A-Za-z0-9_]+ 不允许负号,导致约 50% 数据被拒。但需要意识到:本质上这并不是程序错误,而是数据质量评估的积极成果,因为它揭示了规则与真实数据特征的冲突,为下一阶段迭代指明了方向,这也是为何数据清洗过程被拆分成两个实验项目(A2-1、A2-2)的原因。只有真的实践了,才能认识到给AI算法模型准备原料这份工作的重要性,因此更不能否认大数据技术/大数据分析之前的知识累积在AI时代仍具有较高的价值,毕竟"挖石油"的工作虽然辛苦,却是十分有意义。

前这个筛选结果可以认为是实际应用场景中,数据清洗要面对的真实情况:

(1)清洗规则是过滤器,不是修正工具 。 之前编写各种规则,只会对每行数据做"通过"或"不通过"的判断,不会去改数据本身。通过的打上 OK 标签进 cleaned,不通过的则标记具体原因,归到 rejected。

(2)真实数据到处是坑 。 test_set.csv 是个 1.07GB 的真实气象数据集。一旦程序扫出大量脏数据,基本就说明实际数据质量比预想的差得多。比如:

1)字段缺失:很多观测站可能根本没记录风速(WS10M)或温度(T2M),数值校验必然失败。

2)格式不统一:日期存在多种写法,导致 isValidDate 这类规则直接失效。

3)异常值出现:传感器一抽风,温度或风速就可能远超所设定的 [-50, 50] 或 [0, 100] 这些物理合理范围。

(3)逐字段"一票否决"有待商榷。 代码里用的是 if-else if 短路判断,只要一个字段踩中规则,整行立刻被扔进 rejected,同时附上诊断信息。

**【重要推论】**在实际数据清洗作业中,我们不要妄图一轮操作就能拿到预期的质量合格的数据集,这个过程实际上需要多次、多轮的尝试,期间还要参考一些抽样方法做数据状态预检测,万不能瞧不上这个关键步骤哦!


8. 在实验过程中常见问题与排查记录

现象 根因 解决
yarn node -list 显示 Total Nodes:0 yarn-site.xml 未配置 yarn.resourcemanager.hostname 添加 yarn.resourcemanager.hostname=master
作业提交后立刻失败:MRAppMaster 找不到 未配置 AM 环境变量 mapred-site.xml 中配置 yarn.app.mapreduce.am.env
作业卡在 PREP 状态 默认 AM 请求 1536MB,超过最大分配 配置 yarn.app.mapreduce.am.resource.mb=384
所有 Map 任务失败:InvalidAuxServiceException 未配置 shuffle 辅助服务 yarn-site.xml 中添加 yarn.nodemanager.aux-services
WebUI 显示 Worker 内存仍为 1.5GB 配置文件内容重复,解析器仅读取旧值 cat > 完整覆盖文件
参数个数报 Usage jar 包 Main-Class 与命令行类名冲突 去掉命令行中的类名
打包时"编码 GBK 不可映射字符" 缺少 UTF-8 编译参数 pom.xml 添加 project.build.sourceEncoding=UTF-8
物理机卡死 16GB 总内存被 5 台 VM 占满 关闭非必要应用

9. 实验A2-1总结

(1) 集群配置是系统工程 :RM 地址、AM 内存、Shuffle 服务、环境变量等缺一不可,任一遗漏都会导致作业失败。

(2) jps 不可靠 :进程存在不等于服务可用,必须通过 yarn node -list、WebUI 和实际作业测试验证集群完整性。

(3)低内存环境需严格管控yarn.nodemanager.resource.memory-mb 设为 1024MB,为 OS 保留约 700MB,确保不触发 OOM。

(4)数据清洗是迭代过程 :规则不能一成不变,必须与真实数据对话。A2-1 的探查结果为 A2-2 的策略修正提供了明确方向。

与 A2-2 的衔接和任务预告

A2-2 将基于本阶段的探查结果,实施以下工作:

  • 修正 R2 规则(将正则调整为 -?[0-9]+)。
  • 重新打包运行,验证修正后的清洗效果。
  • 实现 Python Hadoop Streaming 版本。
  • 对比 Java 与 Python 方案的性能与资源消耗。
相关推荐
2601_957190901 小时前
极致沉浸感官体验,超元力重新定义VR枪战竞技新玩法
大数据·人工智能·vr
风虎云龙科研服务器1 小时前
告别几何缩微,拥抱时间优化:韬(τ)定律开启后摩尔时代新周期
大数据·人工智能·深度学习·机器学习·tensorflow
dinl_vin2 小时前
FastAPI 系列 ·(十一):ClickHouse 集成——大数据查询实战
大数据·clickhouse·fastapi
yumgpkpm2 小时前
Hadoop(CDH6、CDP7)在Qwen3.7大模型训练中的作用,(含部署、运行操作步骤)
大数据·hive·hadoop·分布式·zookeeper·spark·kafka
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月26日
大数据·人工智能·python·信息可视化·自然语言处理·ai编程·灵砚智能
做个文艺程序员2 小时前
第09篇:ES 数据同步方案——Canal + Logstash + Flink 全方案对比与实战
大数据·elasticsearch·mysql同步es·es数据同步·flink实时同步·es增量同步
稳如磐石.2 小时前
北京研华上架式工控机
大数据·人工智能·python
mnasd10 小时前
python常用模块
大数据
步里软件10 小时前
2611.某音 MCN 运营效率提升指南:从手动重复到自动化全流程
大数据·自动化·抖音关注·抖音评论