ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Hadoop疾病统计平台搭建:从伪分布式部署到MapReduce作业实战

Hadoop疾病统计平台搭建:从伪分布式部署到MapReduce作业实战 简介基于Hadoop的疾病信息统计平台是面向大数据与Java开发者的完整项目工程围绕医疗疾病数据的采集、存储与并行统计分析构建可支撑课程设计、毕业设计或入门级实战学习。平台以HDFS提供高容错分布式存储借助MapReduce完成大规模计算适合希望理解Hadoop生态协作机制的人群参考。资源压缩后约10.87MB共41个文件其中25个java源码覆盖数据解析与统计逻辑6个xml承担Maven与Hadoop相关配置另有properties、yml环境配置、2个jar依赖和arff样例数据等结构完整可直接导入主流IDE分析。目前已有84人浏览学习。研读这套工程可掌握MapReduce任务从编写到执行的完整链路理解HDFS存储与数据切分思路还能借助依赖与配置文件快速搭建类似的大数据统计实验环境节省自行摸索的时间为疾病分析类场景提供一套可复用的参考实现。1. 一个 Hadoop 疾病统计平台的 ZIP 包里装的是什么解压一个命名类似“基于hadoop的疾病信息统计平台.zip”的项目时很多人的第一反应是双击就能看到统计报表我见过更多人把时间浪费在“数据包是谁给的”而不是“这套链路怎么运转”上。实际上这个 ZIP 装的是课程设计或毕设的整套工程Java 源码、Hadoop 配置、CSV 或数据库备份、造数脚本还有一份写好的说明文档。它的价值不在于“疾病”业务有多复杂而在于它把 HDFS 存储、MapReduce 聚合、结果落盘与可视化这条分布式处理链路完整走了一遍。适用人群很明确没接触过 Hadoop 的在校生、准备实习面试的分析岗候选人以及想拿真实业务场景替代 WordCount 练手的人。锁定这个标题时你要攻下的不是医学知识而是 Hadoop 平台下数据如何建模、上传、统计和验证前三个环节做对了报表展示就只是最后一块拼图。2. 先跑 Hadoop 伪分布式课程设计性价比最高的启动姿势2.1 伪分布式和集群差在哪三种模式先对比再选型Hadoop 有三种部署形态本地模式、伪分布式、完全分布式。课程设计如果把“平台”定位成能上传病例、能跑统计、能出图表最少资源的方案就是伪分布式一个进程承担一个角色NameNode 管元数据DataNode 管文件块ResourceManager 和 NodeManager 管计算资源分配全部挤在一台机器上但进程是独立可见的。伪分布式最容易让人迷糊的点在副本数。完全分布式默认副本数是 3但伪分布式里如果也配置成 3同一个 DataNode 上的同一个文件块会尝试放三份不仅不提升可靠性还会让写操作变慢。课程设计没有生产级容灾需求把dfs.replication显式改成 1 是配置阶段第一件要做的事。完全分布式里主节点配置、从节点分配这些事在伪分布式阶段都被简化成 localhost等你真有三台机器时再改回来也不迟。模式进程分布HDFS典型用途内存要求本地模式单 JVM 内跑 mapper/reducer不启动调试统计逻辑无伪分布式本机多个独立进程启动副本 1课程设计、功能验证2G 以上余量完全分布式三台以上跨机器进程启动副本 3真实数据量、生产每节点 4G 以上如果你后面要接触 HBase 或者开启 NameNode HA不管伪分布式还是完全分布式都绕不开 zookeeper 的整合。课程设计里可以在伪分布式机器上跑单实例 zookeeper把 Leader 选举流程先体验一遍但这不影响疾病统计主流程先记住依赖关系即可别在一开始就让 zookeeper 的配置干扰你的主任务。2.2 在 Ubuntu 上从零安装与配置ZIP 解压、环境变量和 SSH 免密先把课程设计包解开。解压这一步就有一个常见坑Windows 自带的“全部解压”会把 shell 脚本的换行符从 LF 改成 CRLF拷到 Linux 上一执行就报/bin/bash^M。所以我一般把 ZIP 整个传到 Linux 里再解压顺便用file命令检查脚本格式# 解压课程设计包到统一目录-d 指定目标路径 unzip 基于hadoop的疾病信息统计平台.zip -d /opt/disease_platform # 检测脚本里是否混入 CRLF 换行出现 CRLF 就是踩坑信号 file /opt/disease_platform/bin/*.sh | grep CRLF || echo no crlf # 解压 Hadoop 发行版tar.gz 和 zip 都只是打包格式不影响运行 tar -zxvf hadoop-*.tar.gz -C /usr/local/ mv /usr/local/hadoop-* /usr/local/hadoopunzip -d指定输出目录避免把一堆文件直接散到当前目录grep CRLF是为了提前发现换行问题。Hadoop 发行版从官网下载后基本是 tar.gz个别镜像站提供 zip 包解压逻辑一样但需要注意 zip 方式解压出来的脚本未必保留可执行位遇到Permission denied就手动chmod x。环境变量和 SSH 免密是紧接着的两件事。疾病统计平台要起 HDFS 和 YARN免密是启动脚本能无人工干预跑下去的前提# 写入 profile 后记得 source否则当前 shell 找不到 hdfs 命令 export HADOOP_HOME/usr/local/hadoop export PATH$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin export HADOOP_CONF_DIR$HADOOP_HOME/etc/hadoop # 生成本机 SSH 密钥并加入 authorized_keys ssh-keygen -t rsa -b 2048 -P -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys ssh localhost echo ssh_ok-P 表示生成无口令密钥伪分布式本机访问不需要 passphrase-b 2048是 RSA 密钥长度。这里真正拦人的是权限如果.ssh或authorized_keys对其他人可写OpenSSH 会直接忽略密钥表现就是每次执行start-dfs.sh都要输密码。很多人在 Windows 上用 Xshell 连 Linux 后习惯性地把免密配在 Xshell 到 Linux 的会话上这跟 Hadoop 本机之间的免密是两回事别混在一起排查。接下来改四个配置。先看core-site.xml和hdfs-site.xml!-- core-site.xml指定 NameNode 地址和临时目录 -- configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/usr/local/hadoop/tmp/value /property /configuration!-- hdfs-site.xml伪分布式必须把副本数改成 1 -- configuration property namedfs.replication/name value1/value /property /configurationfs.defaultFS决定你后续代码里写的路径是走 HDFS 还是本地文件系统统计代码里的/disease/input这类路径都基于它拼接。hadoop.tmp.dir如果磁盘空间不足NameNode 的 edits 文件和 DataNode 的块数据都会堆积在这建议放到空间充足的路径别和系统根目录抢容量。YARN 的两个参数直接影响作业能否被调度。伪分布式机器内存一般不大把memory-mb设置成虚拟机实际内存的一半以下比较保险!-- yarn-site.xml容器内存规格要小于物理内存 -- configuration property nameyarn.nodemanager.resource.memory-mb/name value4096/value /property property nameyarn.scheduler.minimum-allocation-mb/name value256/value /property /configurationscheduler.minimum-allocation-mb是单个容器请求的最小值默认 128 或 256。如果这个值设得太大比如 1024而作业申请的规格被向上取整就会造成容器资源被浪费后续任务排队。我见过不少把maximum-allocation-mb设成 8192 但机器只有 4G 内存的配置结果 ApplicationMaster 一直起不来这在后面避坑章节会细讲。2.3 格式化与启动格式化的机会只有一次配置做完进入启动阶段。格式化 NameNode 并启动全部进程# 第一次部署才需要格式化之后不要重复执行 hdfs namenode -format start-dfs.sh start-yarn.sh # 确认进程都活着 jpsjps输出里应该看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager 五个进程。如果你的活检报告没有另外说明 DataNode 没注册上这时不要急着重新格式化先去看日志。许多人在启动失败后的第一反应就是再来一次hdfs namenode -format这是最危险的翻车操作第二次格式化会重置 NamespaceID而 DataNode 还保留旧 ID两边一校验就断开结果就是你得到一个永远起不来的簇解决办法反而变成删除 tmp 目录重新来过也就是说格式化机会只有一次是真的。启动完用命令验证 HDFS 可用性hdfs dfsadmin -safemode get hdfs dfs -mkdir -p /disease/input /disease/output hdfs dfs -ls /第一次启动会经历一段安全模式这是 NameNode 在做块报告校验不是故障。如果等了五分钟还在安全模式基本说明 DataNode 没把块报告上报这时重点查logs/hadoop-hadoop-datanode-主机名.log。安全模式期间不能写文件所以很多人的mkdir失败不是权限问题而是安全模式没退出。浏览器验证这一步建议也做NameNode 的 Web UI 在 9870 端口ResourceManager 在 8088 端口能看到活跃节点和文件目录课程设计答辩时展示页面比jps更有说服力。Windows 本机访问时注意/etc/hosts里把主机名映射到虚拟机 IP这是 Xshell 连不上之外另一个常见连接问题。3. 把病例数据送进 HDFS目录规划、字符集与外部建表3.1 先定表结构事实表和维度表分开后面统计才不会返工疾病统计平台的业务主体是“病例记录”围绕它展开的两个维度是“患者”和“病种”。如果把所有字段塞进一张大表比如把患者性别、年龄、居住地、病种名称、传染病类别全写在病例行里后面的 MapReduce 代码确实更好写但耦合度太高病种名称改了你要刷整张表按传染病类别筛选你还得重新解析疾病码。我一般会拆成三张逻辑表表名核心字段用途patient_infopatient_id, gender, birthday, district_code人口维度disease_recordrecord_id, patient_id, disease_code, diagnosis_date, hospital_level病例事实disease_dimdisease_code, disease_name, category病种目录病例事实表是统计的主表MapReduce 直接读取patient_info 和 disease_dim 用于二次关联。性别字段建议统一用M和F比1/2可读性好也避免在 Hive 和 Java 之间来回转换时出现隐式类型问题。日期字段一律存成yyyy-MM-dd字符串Hive 的外部表可直接用substr取年份天然支持按季度聚合。统计口径要提前说清楚按病例事实表聚合出来的叫“发病人次”不是“患病人数”。同一个患者可能因为复诊被登记多次如果平台定位是疾病监测这一点必须在文档里写明。否则答辩老师问你“为什么这个病的人数比卫健委公布的多”你拿着按人次统计的口径就能直接回应。3.2 CSV 上传到 HDFS字符集转换、目录分隔符和块校验课程设计的数据集通常由导师给 CSV或者你自己写脚本生成。Windows 下用 Excel 导出的 CSV 大多是 GBK 编码而 Hadoop 生态默认按 UTF-8 处理直接上传后hdfs dfs -text会看到中文乱码Mapper 里split(,)后字段值也全是问号。正式上传前先转码hdfs dfs -mkdir -p /disease/input /disease/backup # 把 GBK 编码的病例 CSV 转成 UTF-8再执行上传 iconv -f GBK -t UTF-8 disease_record.csv disease_record_utf8.csv hdfs dfs -put disease_record_utf8.csv /disease/input/ # 查看文件块分布确认数据真的落到了 HDFS hdfs fsck /disease/input/disease_record_utf8.csv -files -blocksmkdir -p会自动创建多级目录这里把 input 和 backup 分开是为了后续 mapreduce 输出目录不要和输入目录混在一起。iconv在源文件已经是 UTF-8 时会跳过不要担心重复执行。fsck -files -blocks会列出文件的块信息如果只输出了一个块说明文件小于 128MB如果文件有几百 MB你会看到Block 1、Block 2的条目这能直观验证 HDFS 的切块行为。还有一个容易被忽略的点HDFS 上的目录名不要用中文MapReduce 的Path对中文字符支持并不友好而且在部分 Linux 环境下中文路径的 URI 编码会出偏差。上传命令里的文件名建议改成disease_record.csv这样的英文。如果病例数据量到了 GB 级别hdfs dfs -put还是能用但建议先把 CSV 压缩成 gzipHadoop 本身支持读 gzip能减少网络和磁盘 IO。块大小也可以调但课程设计阶段不用刻意动dfs.block.size默认 128MB 对 MapReduce 的并行度已经足够。3.3 用 Hive 外部表管理病例数据还是直接让 MapReduce 读目录Hive 不是必须的但它能把 HDFS 目录变成一张可查询的表课程设计里用 Hive 做初步探查比较省事。建表时要用EXTERNAL TABLE指向 HDFS 目录CREATE 语句如下CREATE EXTERNAL TABLE IF NOT EXISTS disease_record ( record_id STRING, patient_id STRING, disease_code STRING, diagnosis_date STRING, gender STRING, district_code STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY , STORED AS TEXTFILE LOCATION /disease/input/; SELECT disease_code, gender, count(*) AS cnt FROM disease_record GROUP BY disease_code, gender;LOCATION指向刚才hdfs dfs -put的输入目录Hive 会把目录下的每个文件当作一张表的数据。FIELDS TERMINATED BY ,要和 CSV 实际分隔符一致如果 CSV 里某些字段本身包含逗号这条 SQL 就会错位需要在建表时改成CSVSerDe 或者在上游清洗时给字段加引号。为什么用外部表而不是内部表内部表删掉元数据时底下的 HDFS 文件也会被一起删除课程设计期间的原始病例是稀缺资源删了就没了。外部表解除了这个耦合即使你反复重建表原始 CSV 文件还在 HDFS 上这也是生产环境处理原始数据目录的一贯思路。如果集群里没装 Hive或者你想在答辩时展示 MapReduce 本身的能力直接跳过这层建表也没问题。第 4 章里 Mapper 读到的每一行就是 HDFS 文件的每一行路径、分隔符、字符集约定好Hive 只是给了你一个趁手的查询工具而不是统计链路的必需品。4. 写第一个统计作业Mapper、Reducer 与 YARN 提交4.1 统计口径先定清楚按病种和性别聚合发病人次平台第一版要输出的统计指标我通常选择“病种 性别”的二维聚合。理由很简单它只有一次 shuffle能展示 MapReduce 的核心机制又避免在一开始就引入多表 join。输出格式设计成两列disease_code_gender和count例如I10_M 380含义是 ICD 编码 I10 的疾病在男性病例中出现 380 次。要扩展也很容易把复合键从disease_code_gender改成disease_code_year就按发病年份统计改成district_code_gender就按地区统计。所以这次作业的核心不是业务逻辑而是让你理解“怎么把需要聚合的维度拼成 key”。4.2 可运行的 Java 代码Mapper、Reducer 和 Job 组装用 Maven 工程管理源码建一个DiseaseStatJob.java里面包含一个 Mapper 静态类、一个 Reducer 静态类和 main 方法。以 CSV 输入为例字段顺序假定为record_id,patient_id,disease_code,diagnosis_date,gender,district_codeimport org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException; public class DiseaseStatJob { // 输入 key 是行号value 是一行 CSV 文本 public static class StatMapper extends MapperLongWritable, Text, Text, IntWritable { private Text outKey new Text(); private IntWritable outVal new IntWritable(1); Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line value.toString(); // CSV 表头不是有效病例直接跳过 if (line.startsWith(record_id)) return; String[] cols line.split(,, -1); if (cols.length 6) return; // 复合 key疾病编码 下划线 性别例如 I10_M String diseaseCode cols[2].trim(); String gender cols[4].trim(); outKey.set(diseaseCode _ gender); context.write(outKey, outVal); } } public static class StatReducer extends ReducerText, IntWritable, Text, IntWritable { private IntWritable result new IntWritable(); Override protected void reduce(Text key, IterableIntWritable values, Context context) throws IOException, InterruptedException { int sum 0; for (IntWritable val : values) { sum val.get(); } result.set(sum); context.write(key, result); } } public static void main(String[] args) throws Exception { if (args.length ! 2) { System.err.println(Usage: DiseaseStatJob input output); System.exit(-1); } Configuration conf new Configuration(); Job job Job.getInstance(conf, disease count by code gender); job.setJarByClass(DiseaseStatJob.class); job.setMapperClass(StatMapper.class); // 求和是幂等操作combiner 可以复用 reducer 逻辑 job.setCombinerClass(StatReducer.class); job.setReducerClass(StatReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } }复合 key 用下划线连接疾病编码和性别是为了避免在 Shuffle 阶段切成两个字段后还要二次排序。split(,, -1)里的-1参数保证保留尾部空字符串防止最后几个字段为空时被裁剪导致数组长度不够。每个字段都做了trim()是为了清掉 Windows 文本里常见的\r尾随字符。Combiner 在这里直接复用 Reducer 逻辑因为求和操作是幂等的在 Map 端本地先合并一遍能减少网络传输如果是求平均值或者按比例计算的指标就不能直接复用 Reducer得单独写 Combiner。这是答辩时经常被追问的点。4.3 编译打包并提交到 YARN命令和三个底层步骤不用 Maven 的话可以用hadoop classpath手动编译这一步在课程设计里最省事# 使用 Hadoop 自带的 classpath 编译源码 javac -classpath $(hadoop classpath) -d ./classes DiseaseStatJob.java # 打成 jar 包注意包含主类 jar -cvf disease-stat.jar -C classes . # 提交作业到 YARN输出目录必须是不存在的路径 hadoop jar disease-stat.jar DiseaseStatJob /disease/input /disease/output/code_genderhadoop jar会把 jar 包提交到 YARN 的 ApplicationMaster。作业提交的完整流程分三步客户端先把 jar 包和作业切片信息上传到 HDFS 的暂存目录然后 ResourceManager 收到请求后为这个应用分配第一个容器最后容器里启动 ApplicationMaster由它申请后续的 Map 和 Reduce 容器。这里最容易踩的坑是输出目录已经存在。MapReduce 的输出目录如果已存在会直接报FileAlreadyExistsException所以重复跑同一个作业前要么删除旧输出目录要么每次换一个新的输出目录名。作业跑完后验证结果hdfs dfs -cat /disease/output/code_gender/part-r-00000 | head -20输出目录下通常有part-r-00000到part-r-0000n多个文件每个对应一个 Reducer。如果你设置了多个 Reducer 但统计结果被分散到不同文件答辩时可以用hdfs dfs -getmerge合并后再展示避免给人“数据不完整”的错觉。5. 避坑清单从 ZIP 解压到结果落盘的五个高频翻车点5.1 ZIP 解压后脚本报/bin/bash^M中文文件名乱码现象Linux 上执行解压出来的脚本提示bad interpreter: /bin/bash^M或者unzip之后文件名变成一串乱码CSV 内容用cat看是正常的但进入 Java 处理就变问号。原因Windows 的压缩工具把文本文件换行写成 CRLF也常把压缩包内文件名按本地代码页写入Linux 的 unzip 默认按 UTF-8 解出文件名两边对不上。解决尽量把 ZIP 传到 Linux 再解压不要从本地拖解压后的文件。对已经有问题的包用dos2unix批量转换脚本换行unzip -O gbk 基于hadoop的疾病信息统计平台.zip -d /opt/disease_platform find /opt/disease_platform -name *.sh -exec dos2unix {} \;-O gbk只有部分 unzip 版本支持不支持时可以改用 7z 指定编码。如果你遇到的是伪加密的 zip文件头标志位被改成加密其实数据本身没有加密7z 通常能直接打开如果是真加密直接找作者要密码别在暴力破解上浪费时间。5.2 SSH 免密失效每次 start-dfs.sh 都要输入密码现象ssh localhost命令交互式地提示输入密码启动脚本在分发进程时卡住或反复询问认证。原因最常见的是~/.ssh目录或authorized_keys权限过宽OpenSSH 为了安全会拒读这类密钥文件另一类是系统里开了 SELinux 或防火墙拦截了 22 端口。解决先修正权限再重试并把 StrictHostKeyChecking 关闭以避免首次连接的人机确认chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys ssh-keygen -R localhost ssh -o StrictHostKeyCheckingno localhost echo ok注意区分场景如果你在 Windows 上用 Xshell 连 Linux然后把免密配到了 Xshell 的会话上这不会影响 Hadoop 脚本在 Linux 本机发起的 SSH。Hadoop 要用的是 Linux 到 Linux 本机的免密这套关系要在 Linux 系统内部配置好别在 Xshell 客户端里找原因。5.3 DataNode 起不来NameNode 一直处于安全模式现象jps看不到 DataNode 进程HDFS 目录能ls但mkdir报Name node is in safe mode日志里出现 clusterID 不一致的提示。原因你在多次初始化过程中重复执行了hdfs namenode -format。每次格式化都会生成新的 NamespaceID而 DataNode 的current/VERSION文件里还是旧 IDDataNode 和 NameNode 握手失败它就直接退出NameNode 收不到完整块报告也就永远退不出安全模式。解决停止所有 Hadoop 进程删除 NameNode 和 DataNode 的临时目录然后重新格式化一次再启动stop-all.sh rm -rf /usr/local/hadoop/tmp/dfs hdfs namenode -format start-dfs.sh这条命令组合只适用于课程设计阶段的数据生产环境里的 HDFS 有业务数据删除目录等于删数据。生产环境遇到类似问题要用hdfs namenode -recover做元数据恢复而不是直接删。所以你该记住的教训是格式化只能做一次除非你确定所有节点上的存储目录都同步清干净了。5.4 作业提交到 YARN 后一直 ACCEPTED迟迟不 RUNNING现象客户端提示Submitting application to ResourceManager但 Map 进度一直 0%8088 端口看到状态是 ACCEPTED容器数持续为 0。原因资源不够或资源规格配置超出节点容量。比如把yarn.nodemanager.resource.memory-mb设成 4096但虚拟机只有 2G 内存或者yarn.scheduler.minimum-allocation-mb配置太大使得 AM 申请最小容器时都无法满足。解决用yarn node -list -all查看节点真实资源和可用资源然后调低 yarn-site.xml 里的三个参数property nameyarn.nodemanager.resource.memory-mb/name value2048/value /property property nameyarn.scheduler.minimum-allocation-mb/name value256/value /property property nameyarn.app.mapreduce.am.resource.mb/name value512/value /property改动后重启 YARN 相关进程再提交。还有一个隐藏影响因素是主机名解析ResourceManager 拿不到节点的主机名时会认为节点 lost也会导致容器一直申请不出来这时要在/etc/hosts里补上本机 IP 和主机名的映射。5.5 统计结果 0 行或漏掉某个疾病分组现象作业正常结束Counter 显示 Map 输入记录为 0或者某几个疾病类别的分组完全没出现在输出文件里。原因最常见的有三个。一是上传的文件名以_或.开头例如_disease_record.csvMapReduce 的输入分片机制默认忽略这类文件认为是 Hadoop 内部临时文件二是 CSV 表头带来的干扰Mapper 判断条件没写对直接把整行当成脏数据过滤三是在 Windows 上编辑过 CSV行尾的\r让最后一个字段带着不可见字符复合 key 拼接后和预期不一致。解决先用hdfs dfs -ls /disease/input看目录里是否有下划线开头的文件有就重命名。接着检查 Counter 里的记录数如果 Map input records 比文件行数少一行说明表头被处理了日志里也可能有异常。最后在写 Mapper 和 Reducer 时对所有字段做trim()并打一行日志验证 key 格式这样可以定位是否被\r污染。如果作业是通过重跑多次才得到结果的注意输出目录下可能残留_temporary目录里面也有部分中间结果。不要对着_temporary里的文件统计规范做法是只用part-r-*文件其他都视为中间产物。6. 用 YARN 日志和 Counter 校准统计结果的隐藏边界6.1 从 Counter 判断数据是否被正确消费作业跑完先别急着看part-r-00000先看 Map-Reduce Framework 的 Counter。命令行可以直接取指定计数器的值hadoop job -counter job_1710000000000_0001 \ File Input Format Counters Bytes Read hadoop job -counter job_1710000000000_0001 \ Map-Reduce Framework Map output records对比 File Input Format Counters 里的Bytes Read和 Map output records能很快判断 Mapper 过滤了多少数据。如果 Bytes Read 有值但 Map output records 是 0问题一定在 Mapper 的过滤条件上如果两者都明显小于文件实际大小说明输入路径下的文件没被完整读入大概率踩了 5.5 里说的下划线文件坑。Reduce input records 小于 Map output records 是 Combiner 生效的正常现象不是数据丢失。6.2 用日志反推口径漂移点YARN 把每个容器的标准输出和错误聚合成一份应用日志可以用命令行抽出来定向排查yarn logs -applicationId application_1710000000000_0001 \ | grep -E (sample_key|Exception) | head -50我会在 Mapper 里加上一条临时的调试输出把复合 key 打到 System.err然后跑一个小数据量样本。这样能直观看到每个 key 长什么样I10_M字符串之间有没有多余空格、性别字段是M还是男、日期字段是不是真实按yyyy-MM-dd进入 key。数据字段的格式问题在很多“结果看起来不对”的案例里都是最后通过这行日志现形的。这套方法的另一个价值是验证统计口径是否稳定。比如你把复合 key 从病种性别改成病种年龄组时如果日志显示的 key 分布和病种维度表里的字典值不一致说明 CSV 有病种编码的脏数据。通过 Counter 的Map output records与Reduce output records的差值能反推出有多少记录在 Shuffle 阶段被过滤这个差值结合日志比直接盯着结果表更容易定位原因。我早年在做类似统计平台时习惯写完 Mapper 直接提交全量作业结果某个病种分组消失了两天才发现是字段尾部的\r在作怪。后来学乖了第一轮永远是小样本加调试日志加 Counter 校验确认口径无误再提交全量。希望这个顺序也能帮到你少走一段弯路。本文还有配套的精品资源点击获取
返回列表