《前后端面试题》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,SQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [106. 如何用Java编写一个简单的MapReduce程序?(如WordCount)](#106. 如何用Java编写一个简单的MapReduce程序?(如WordCount))
- [107. 描述一个使用Hadoop处理实际业务场景的案例(如日志分析、数据统计等)](#107. 描述一个使用Hadoop处理实际业务场景的案例(如日志分析、数据统计等))
- [108. Hadoop与云计算平台(如AWS EMR、阿里云EMR)的关系是什么?](#108. Hadoop与云计算平台(如AWS EMR、阿里云EMR)的关系是什么?)
- [109. 如何实现Hadoop集群与关系型数据库的数据同步?](#109. 如何实现Hadoop集群与关系型数据库的数据同步?)
- [110. 结合Hadoop生态工具,设计一个数据处理 pipeline(如采集→存储→分析→可视化)](#110. 结合Hadoop生态工具,设计一个数据处理 pipeline(如采集→存储→分析→可视化))
-
- 二、110道Hadoop面试题目录列表
一、本文面试题目录
106. 如何用Java编写一个简单的MapReduce程序?(如WordCount)
MapReduce是Hadoop的分布式计算框架,核心思想是将任务拆分为Map和Reduce两个阶段。以下以经典的WordCount(统计文本中单词出现次数)为例,说明Java编写MapReduce程序的步骤。
原理说明:
- Map阶段:读取输入文本,将每行拆分为单词,输出键值对(单词,1)。
- Reduce阶段:接收Map的输出,将相同单词的计数累加,输出(单词,总次数)。
示例代码:
- Map类(继承Mapper):
java
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
// 输入键:行偏移量(LongWritable),输入值:行内容(Text)
// 输出键:单词(Text),输出值:计数1(IntWritable)
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private static final IntWritable ONE = new IntWritable(1);
private Text word = new Text();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
// 拆分每行文本为单词(按空格分割)
String[] words = value.toString().split(" ");
for (String w : words) {
word.set(w);
// 输出(单词,1)
context.write(word, ONE);
}
}
}
- Reduce类(继承Reducer):
java
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
// 输入键:单词(Text),输入值:计数列表(IntWritable)
// 输出键:单词(Text),输出值:总次数(IntWritable)
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
int sum = 0;
// 累加相同单词的计数
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
// 输出(单词,总次数)
context.write(key, result);
}
}
- 主类(配置Job):
java
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;
public class WordCount {
public static void main(String[] args) throws Exception {
// 加载Hadoop配置
Configuration conf = new Configuration();
// 创建Job实例
Job job = Job.getInstance(conf, "word count");
// 设置主类
job.setJarByClass(WordCount.class);
// 设置Map和Reduce类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
// 设置输出键值对类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// 设置输入和输出路径(从命令行参数获取)
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
// 提交Job并等待完成
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
运行步骤:
-
编译打包为JAR文件(如
wordcount.jar)。 -
在HDFS上创建输入目录并上传文本文件:
bashhdfs dfs -mkdir /input hdfs dfs -put /local/path/text.txt /input -
运行MapReduce任务:
bashhadoop jar wordcount.jar WordCount /input /output -
查看结果:
bashhdfs dfs -cat /output/part-r-00000
107. 描述一个使用Hadoop处理实际业务场景的案例(如日志分析、数据统计等)
以电商平台用户行为日志分析为例,说明Hadoop在实际业务中的应用。该场景需从海量用户日志中提取关键指标(如页面访问量、用户留存率、转化率),为运营决策提供数据支持。
场景背景:
- 日志来源:用户访问网站时产生的行为日志(如点击、浏览、下单),每天产生约100GB数据,存储在服务器本地。
- 分析目标:统计每日各页面的PV(页面浏览量)、UV(独立访客数),分析用户从浏览到下单的转化率。
技术方案:
- 数据采集:使用Flume实时收集分布在多台服务器的日志,写入HDFS。
- 数据存储 :HDFS存储原始日志(按日期分区,如
/logs/2024-05-01/)。 - 数据清洗与转换:使用MapReduce或Spark清洗日志(去除无效数据、提取关键字段),输出结构化数据(如用户ID、页面ID、行为类型、时间戳)。
- 数据分析 :
- 用Hive编写HQL统计PV、UV(按页面和日期分组)。
- 用Spark分析用户行为路径,计算转化率(如下单用户数/浏览用户数)。
- 结果可视化:将分析结果导入MySQL,通过BI工具(如Tableau)生成可视化报表。
关键实现步骤:
-
Flume日志采集配置 (
flume-conf.properties):propertiesagent.sources = logSource agent.channels = memoryChannel agent.sinks = hdfsSink # 监控本地日志文件 agent.sources.logSource.type = exec agent.sources.logSource.command = tail -F /var/log/ecommerce/access.log agent.sources.logSource.channels = memoryChannel # 内存通道 agent.channels.memoryChannel.type = memory agent.channels.memoryChannel.capacity = 10000 # 写入HDFS agent.sinks.hdfsSink.type = hdfs agent.sinks.hdfsSink.hdfs.path = hdfs://namenode:9000/logs/%Y-%m-%d/ agent.sinks.hdfsSink.hdfs.filePrefix = access- agent.sinks.hdfsSink.hdfs.rollInterval = 3600 # 每小时生成一个文件 agent.sinks.hdfsSink.channel = memoryChannel -
Hive分析PV和UV:
sql-- 创建原始日志表(外部表,指向HDFS日志路径) CREATE EXTERNAL TABLE user_log ( user_id STRING, page_id STRING, action STRING, -- 'view', 'click', 'order' time STRING ) PARTITIONED BY (dt STRING) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/logs/'; -- 添加分区(每日执行) ALTER TABLE user_log ADD PARTITION (dt='2024-05-01') LOCATION '/logs/2024-05-01/'; -- 统计PV(按页面和日期) CREATE TABLE page_pv AS SELECT dt, page_id, COUNT(*) AS pv FROM user_log WHERE dt='2024-05-01' GROUP BY dt, page_id; -- 统计UV(按页面和日期,去重用户ID) CREATE TABLE page_uv AS SELECT dt, page_id, COUNT(DISTINCT user_id) AS uv FROM user_log WHERE dt='2024-05-01' GROUP BY dt, page_id; -
Spark分析转化率:
scalaimport org.apache.spark.sql.SparkSession object ConversionRate { def main(args: Array[String]): Unit = { val spark = SparkSession.builder() .appName("ConversionRate") .getOrCreate() // 读取Hive表数据 val df = spark.sql("SELECT user_id, action FROM user_log WHERE dt='2024-05-01'") // 统计浏览用户数 val viewUsers = df.filter("action='view'").select("user_id").distinct().count() // 统计下单用户数 val orderUsers = df.filter("action='order'").select("user_id").distinct().count() // 计算转化率 val conversionRate = orderUsers.toDouble / viewUsers.toDouble println(s"2024-05-01 转化率: ${conversionRate * 100}%") spark.stop() } }
价值:通过Hadoop生态工具处理海量日志,实现了低成本、高可扩展的用户行为分析,帮助运营团队优化页面设计、提升转化效率。
108. Hadoop与云计算平台(如AWS EMR、阿里云EMR)的关系是什么?
Hadoop与云计算平台(如AWS EMR、阿里云EMR)是技术基础与商业化服务的关系,云计算平台基于Hadoop生态提供托管式服务,降低了Hadoop的部署和运维成本。
关系解析:
-
Hadoop是技术内核
云计算平台的EMR(Elastic MapReduce)服务以Hadoop为核心,集成了HDFS、YARN、MapReduce、Spark、Hive等组件,提供分布式计算和存储能力。例如:
- AWS EMR和阿里云EMR的底层计算引擎依赖Hadoop YARN,存储依赖HDFS(或兼容的对象存储如S3、OSS)。
-
云计算平台提供托管服务
云计算平台简化了Hadoop的部署、运维和扩展:
- 自动化部署:通过控制台或API一键创建Hadoop集群,无需手动配置SSH、JDK、Hadoop参数。
- 弹性伸缩:根据任务负载自动增减节点(如AWS EMR的Auto Scaling),避免资源浪费。
- 高可用性:内置HA架构(如多可用区部署NameNode),提供数据备份和故障自动恢复。
- 集成云服务:与对象存储(S3/OSS)、数据库(RDS)、监控工具(CloudWatch/云监控)无缝集成。
-
适用场景互补
- 自建Hadoop集群:适合对数据隐私要求高、长期稳定运行的场景(如企业内部数据中心)。
- 云EMR服务:适合短期项目、峰值波动大的场景(如临时数据分析、电商大促期间的日志处理),可按需付费。
示例 :
使用阿里云EMR运行Hive任务:
-
在阿里云控制台创建EMR集群(选择Hive、Spark组件)。
-
将数据上传至阿里云OSS(兼容HDFS API)。
-
通过EMR的Hive客户端执行查询:
sqlSELECT COUNT(*) FROM oss://my-bucket/logs/dt=2024-05-01; -
任务完成后释放集群,仅按实际使用时长付费。
109. 如何实现Hadoop集群与关系型数据库的数据同步?
Hadoop集群与关系型数据库(如MySQL、Oracle)的数据同步需解决批量数据传输 和增量更新问题,常用工具包括Sqoop、DataX和自定义脚本。
同步方案:
-
全量同步(批量导入/导出)
使用Sqoop实现Hadoop与数据库的全量数据传输。
-
从数据库导入HDFS/Hive :
bash# 导入MySQL的employee表到HDFS sqoop import \ --connect jdbc:mysql://db-host:3306/company \ --username root \ --password password \ --table employee \ --target-dir /user/hadoop/employee \ --fields-terminated-by '\t' \ --m 4 # 4个Map任务并行导入 -
从HDFS导出到数据库 :
bash# 将HDFS数据导出到MySQL的employee_result表 sqoop export \ --connect jdbc:mysql://db-host:3306/company \ --username root \ --password password \ --table employee_result \ --export-dir /user/hadoop/employee_result \ --input-fields-terminated-by '\t'
-
-
增量同步(实时/准实时)
-
基于时间戳 :通过Sqoop的
--incremental参数,仅同步时间戳大于上次同步的记录:bashsqoop import \ --connect jdbc:mysql://db-host:3306/company \ --table employee \ --incremental lastmodified \ --check-column update_time \ # 时间戳字段 --last-value '2024-05-01 00:00:00' \ # 上次同步的时间戳 --target-dir /user/hadoop/employee_incremental -
基于Binlog :使用Canal监听数据库Binlog(二进制日志),实时捕获数据变更并同步到Kafka,再通过Flink/Spark Streaming写入HDFS/HBase。
- 优势:低延迟(毫秒级),适合实时数据同步。
- 架构:
MySQL Binlog → Canal → Kafka → Flink → HDFS。
-
-
开源工具DataX
DataX是阿里开源的异构数据同步工具,支持Hadoop与数据库的同步,配置更灵活:
-
编写JSON配置文件(
mysql2hdfs.json):json{ "job": { "content": [ { "reader": { "name": "mysqlreader", "parameter": { "username": "root", "password": "password", "connection": [ { "querySql": ["SELECT id, name FROM employee WHERE dt='2024-05-01'"], "jdbcUrl": ["jdbc:mysql://db-host:3306/company"] } ] } }, "writer": { "name": "hdfswriter", "parameter": { "defaultFS": "hdfs://namenode:9000", "path": "/user/hadoop/employee_datax", "fileName": "employee", "writeMode": "append", "fieldDelimiter": "\t" } } } ] } } -
运行同步任务:
bashpython datax.py mysql2hdfs.json
-
110. 结合Hadoop生态工具,设计一个数据处理 pipeline(如采集→存储→分析→可视化)
一个完整的数据处理 pipeline 需覆盖数据从产生到价值输出的全流程,结合 Hadoop 生态工具可实现高可用、可扩展的大规模数据处理。以下以电商用户行为分析 pipeline为例,详细说明各环节设计。
整体架构
数据来源 → 采集(Flume/Kafka) → 存储(HDFS/HBase) → 清洗转换(Spark) → 分析(Hive/Spark SQL) → 可视化(Superset)
各环节功能及工具选择:
- 采集:实时收集多源数据(Web日志、App埋点、数据库变更)。
- 存储:分层存储原始数据和处理后数据,兼顾成本和访问效率。
- 清洗转换:处理脏数据、标准化格式、提取关键指标。
- 分析:通过SQL或编程实现统计分析、用户画像等。
- 可视化:将分析结果以图表形式展示,辅助决策。
详细实现
1. 数据采集阶段
目标 :实时/准实时收集分散的用户行为数据(如页面浏览、点击、下单)。
工具:Flume(日志收集)、Kafka(消息缓冲)。
-
Flume配置(收集Nginx日志):
properties# agent名称:log-agent log-agent.sources = nginx-source log-agent.channels = kafka-channel log-agent.sinks = kafka-sink # 源:监控Nginx日志文件 log-agent.sources.nginx-source.type = exec log-agent.sources.nginx-source.command = tail -F /var/log/nginx/access.log log-agent.sources.nginx-source.channels = kafka-channel # 通道:内存通道(临时缓冲) log-agent.channels.kafka-channel.type = memory log-agent.channels.kafka-channel.capacity = 100000 # sink:发送到Kafka主题(user-behavior) log-agent.sinks.kafka-sink.type = org.apache.flume.sink.kafka.KafkaSink log-agent.sinks.kafka-sink.kafka.bootstrap.servers = kafka-broker1:9092,kafka-broker2:9092 log-agent.sinks.kafka-sink.kafka.topic = user-behavior log-agent.sinks.kafka-sink.channel = kafka-channel -
Kafka作用 :
作为缓冲层,解决数据峰值压力(如秒杀活动日志激增),同时为下游多个消费者(如Spark Streaming、Flink)提供数据接入。
2. 数据存储阶段
目标 :分层存储数据,满足不同场景需求。
工具:HDFS(原始数据)、HBase(实时查询)。
-
HDFS存储:
-
存储原始日志(通过Spark Streaming从Kafka消费后写入),按日期分区:
scala// Spark Streaming消费Kafka数据并写入HDFS val kafkaDF = spark.readStream .format("kafka") .option("kafka.bootstrap.servers", "kafka-broker1:9092") .option("subscribe", "user-behavior") .load() // 解析日志并写入HDFS(按日期分区) kafkaDF.selectExpr("CAST(value AS STRING)") .writeStream .format("parquet") .option("path", "hdfs://namenode:9000/user/data/raw/user-behavior/") .option("checkpointLocation", "/user/checkpoint/") .partitionBy("dt") // dt为日期字段(如2024-05-01) .start()
-
-
HBase存储:
-
存储高频访问的用户行为指标(如用户最近登录时间、累计下单次数),支持随机查询:
shell# 创建HBase表(user_metrics,列族:stats) create 'user_metrics', 'stats' # 插入数据(行键:user_id,列:stats:login_time、stats:order_count) put 'user_metrics', 'user_123', 'stats:login_time', '2024-05-01 10:30:00' put 'user_metrics', 'user_123', 'stats:order_count', '5'
-
3. 数据清洗与转换阶段
目标 :处理脏数据(如缺失字段、格式错误),提取结构化指标(如用户ID、行为类型、时间戳)。
工具:Spark(批处理/流处理)。
-
Spark批处理清洗 (处理HDFS中的历史数据):
scala// 读取HDFS中的原始日志(Parquet格式) val rawDF = spark.read.parquet("hdfs://namenode:9000/user/data/raw/user-behavior/dt=2024-05-01/") // 清洗逻辑:过滤无效数据、提取字段 val cleanDF = rawDF .selectExpr("split(value, ' ')[0] as ip", // 从日志中提取IP "split(value, ' ')[3] as time", // 提取时间 "split(value, ' ')[6] as url") // 提取访问URL .filter("url is not null") // 过滤URL为空的记录 .withColumn("dt", lit("2024-05-01")) // 添加日期分区 // 写入清洗后的数据到HDFS(供后续分析) cleanDF.write.parquet("hdfs://namenode:9000/user/data/clean/user-behavior/dt=2024-05-01/")
4. 数据分析阶段
目标 :通过SQL或编程实现业务指标分析(如PV/UV、转化率、用户留存率)。
工具:Hive(SQL分析)、Spark SQL(复杂分析)。
-
Hive分析PV/UV:
sql-- 创建外部表关联清洗后的数据 CREATE EXTERNAL TABLE user_behavior_clean ( ip STRING, time STRING, url STRING ) PARTITIONED BY (dt STRING) STORED AS PARQUET LOCATION 'hdfs://namenode:9000/user/data/clean/user-behavior/'; -- 添加分区 ALTER TABLE user_behavior_clean ADD PARTITION (dt='2024-05-01'); -- 统计每日PV(页面浏览量) SELECT dt, COUNT(*) as pv FROM user_behavior_clean WHERE dt='2024-05-01' GROUP BY dt; -- 统计每日UV(独立访客数,基于IP去重) SELECT dt, COUNT(DISTINCT ip) as uv FROM user_behavior_clean WHERE dt='2024-05-01' GROUP BY dt; -
Spark SQL分析用户留存率:
scala// 计算次日留存率:首日登录用户中,次日再次登录的比例 val day1Users = spark.sql("SELECT DISTINCT ip FROM user_behavior_clean WHERE dt='2024-05-01'") val day2Users = spark.sql("SELECT DISTINCT ip FROM user_behavior_clean WHERE dt='2024-05-02'") val retainedUsers = day1Users.join(day2Users, Seq("ip"), "inner") val retentionRate = retainedUsers.count().toDouble / day1Users.count().toDouble println(s"2024-05-01 次日留存率: ${retentionRate * 100}%")
5. 数据可视化阶段
目标 :将分析结果以直观图表展示(如折线图、柱状图)。
工具:Apache Superset(开源BI工具)、Tableau。
- 实现步骤 :
-
将Hive/Spark SQL的分析结果导出到MySQL(通过Sqoop):
bashsqoop export \ --connect jdbc:mysql://bi-db:3306/bi_report \ --username bi_user \ --password bi_pass \ --table pv_uv_report \ --export-dir /user/data/analysis/pv_uv/ \ --input-fields-terminated-by '\t' -
在Superset中连接MySQL数据源,创建仪表盘:
- 折线图展示每日PV/UV趋势。
- 饼图展示各页面访问占比。
- 指标卡展示转化率、留存率等核心指标。
-
总结
该 pipeline 利用Hadoop生态工具的协同能力,实现了从海量用户行为数据到业务洞察的全流程处理:
- 实时性:通过Flume+Kafka+Spark Streaming实现准实时数据接入。
- 可扩展性:HDFS和Kafka支持水平扩展,适应数据量增长。
- 灵活性:结合SQL(Hive)和编程(Spark)满足不同分析需求。
- 易用性:通过Superset降低可视化门槛,让非技术人员也能获取数据价值。