大数据计算资源基础知识
大数据处理基本背景
大数据处理面临的主要挑战是数据量太大,无法在单台机器上高效处理。因此,需要分布式系统将数据和计算任务分散到多台机器上协同完成。根据处理方式和应用场景的不同,发展出了不同类型的计算资源。
批处理计算资源
背景:最早的大数据处理方式,主要处理已经存储好的大量历史数据。
-
MapReduce:
- 背景:由Google在2004年提出的分布式计算模型,Hadoop是其开源实现
- 工作原理:将数据处理拆分为"映射(Map)"和"归约(Reduce)"两个阶段,可以在大量普通计算机上并行处理
- 适用场景:如每日的销售报表、网站访问日志分析等不需要实时结果的场景
-
Hive:
- 背景:由Facebook开发,为了让不懂编程的数据分析师也能处理大数据
- 工作原理:提供类SQL语言(HQL)接口,底层转换为MapReduce任务执行
- 适用场景:数据仓库建设、复杂数据查询、报表生成
-
Spark Batch:
- 背景:为克服MapReduce的局限性而开发,最初由UC Berkeley开发
- 工作原理:采用内存计算,避免中间结果写入磁盘,大幅提高处理速度
- 适用场景:需要多次迭代的复杂计算,如机器学习算法训练
流处理计算资源
背景:随着实时性需求的增加,开始出现处理实时数据流的技术。
-
Flink:
- 背景:起源于欧洲的研究项目,后成为Apache顶级项目
- 工作原理:设计为真正的流处理引擎,以事件为单位处理数据
- 适用场景:银行交易欺诈实时检测、网站实时用户行为分析
-
Spark Streaming:
- 背景:Spark生态的一部分,为满足实时计算需求而设计
- 工作原理:将数据流分割成小批次,利用Spark批处理能力处理
- 适用场景:社交媒体情感分析、物联网数据实时处理
-
Storm:
- 背景:最早由Twitter开发和使用的流处理系统
- 工作原理:通过"拓扑"定义处理流程,保证毫秒级响应
- 适用场景:实时计数、系统异常监控告警
交互式查询资源
背景:为满足数据分析师对大数据的快速查询需求而产生。
-
Presto/Trino:
- 背景:最初由Facebook开发,为了解决Hive查询速度慢的问题
- 工作原理:采用内存处理查询,不依赖MapReduce,支持多数据源
- 适用场景:业务人员即席查询、跨数据源的数据分析
-
Impala:
- 背景:由Cloudera开发,受Google Dremel启发
- 工作原理:采用MPP(大规模并行处理)架构,直接访问HDFS数据
- 适用场景:交互式BI工具、数据探索分析
-
Kylin:
- 背景:最初由eBay开发,用于加速OLAP分析
- 工作原理:预先计算并存储多维数据立方体,实现超快查询
- 适用场景:销售数据多维分析、用户行为分析报表
机器学习/AI计算资源
背景:随着人工智能的发展,需要专门的框架处理机器学习任务。
-
Spark MLlib:
- 背景:Spark生态的机器学习库,统一在Spark平台上开发
- 工作原理:利用Spark分布式计算能力,实现常见机器学习算法
- 适用场景:客户分群、产品推荐、风险评估
-
TensorFlow/PyTorch:
- 背景:分别由Google和Facebook开发的深度学习框架
- 工作原理:通过计算图或动态图定义模型,支持GPU加速
- 适用场景:图像识别、语音转文字、自然语言处理、智能客服
弹性计算资源
背景:为优化资源利用率、降低成本而产生的资源调度系统。
-
K8s集群:
- 背景:由Google开源的容器编排平台,现已成为云原生标准
- 工作原理:管理容器化应用的部署、扩展和运行
- 适用场景:微服务架构应用、DevOps持续集成部署
-
Yarn集群:
- 背景:Hadoop 2.0引入的资源管理器,解决Hadoop 1.0的资源利用问题
- 工作原理:负责集群资源分配和作业调度
- 适用场景:在同一集群上运行多种计算框架(MapReduce、Spark等)
大数据核心技术详解
Hadoop
核心概念
Hadoop是一个开源的分布式计算框架,设计用来处理无法在单台机器上高效处理的大数据。
两大核心组件
-
HDFS (存储)
- 分布式文件系统,将大文件分块存储在多台机器上
- 一个中心管理器(NameNode)和多个存储节点(DataNode)
- 自动多副本机制确保数据安全
-
MapReduce (计算)
- 分布式计算模型,分为Map和Reduce两个阶段
- Map:将任务分解为小块并行处理
- Reduce:汇总处理结果
工作原理简图
[大数据] → [HDFS分块存储] → [MapReduce计算]
| |
[多台服务器] [并行处理]
| |
[自动容错] [结果汇总] → [最终结果]
Hadoop优势
- 可扩展性:从几台到上千台服务器
- 容错性:自动处理节点故障
- 成本效益:可使用普通硬件
- 生态系统丰富:Hive、HBase、Spark等工具集成
适用场景
- 日志处理分析
- 数据仓库建设
- 搜索引擎索引构建
- 机器学习数据准备
理解Hadoop就是理解"分而治之"的思想——将大问题拆分为小问题并行解决。
MapReduce
核心概念
- 一种分布式计算模型,由Google在2004年提出
- 设计用于在大型计算机集群上并行处理海量数据
两大核心阶段
-
Map阶段
- 将输入数据分片,每片由一个Mapper处理
- 每个Mapper输出键值对<Key, Value>
- 例如:文本分割成<单词, 1>的形式
-
Reduce阶段
- 接收具有相同Key的所有Value
- 合并计算后输出最终结果
- 例如:统计<单词, 出现次数>
工作原理简图
[输入数据分片] → [Map处理] → [Shuffle] → [Reduce处理] → [结果]
| | | |
[多台服务器] [转化为K-V] [分组] [聚合计算]
适用场景
- 日志分析
- 搜索索引
- 数据转换
- 简单统计计算
编程模型示例
// WordCount示例
// WordCount示例 - MapReduce框架下统计文本中单词出现频率的标准实现
/* ===== MapReduce工作流程 =====
* 1. 输入分片: HDFS将输入文件分成固定大小的块(通常128MB)
* 2. Map阶段: 每个分片由一个Map任务处理,产生中间键值对
* 3. Shuffle阶段: 系统自动对Map输出进行排序、分区、合并,相同key的值被发送到同一个Reducer
* 4. Reduce阶段: 对每个key的所有值进行处理,产生最终结果
* 5. 输出: 将Reduce结果写入HDFS
*/
public class WordCount {
// Mapper类实现 - MapReduce第一阶段
// Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
// 静态常量值1,在所有map调用间共享,避免重复创建对象
private final static IntWritable one = new IntWritable(1);
// 可重用的Text对象,避免垃圾回收压力
private Text word = new Text();
// map方法 - 此方法会被框架对每条输入记录调用一次
// 每个map任务会处理一个输入分片(split)的多条记录
//context是一个非常重要的对象,它代表了map或reduce任务的执行h环境,是Mapper/Reducer与MapReduce框架之间的桥梁
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
// 在MapReduce中,map(20, Text("Hello Hadoop"), context) 中的"20"是输入键(key),代表该文本行在原始输入文件中的字节偏移量(byte offset)。
// 详细解释:
// 当MapReduce处理文本文件时,它会:
// 将文件分割成行
// 对每一行调用一次map方法
// 第一个参数是这一行在文件中的起始位置(偏移量)
// 例如:
// map(0, Text("Hello World"), context) - 第一行从文件的第0个字节开始
// map(20, Text("Hello Hadoop"), context) - 第二行从文件的第20个字节开始(假设第一行加上换行符共20字节)
// 在实际的map函数实现中:
// 这个偏移量很少被直接使用,大多数WordCount实现中会忽略它
// 它的主要作用是提供数据来源的位置信息,在某些应用中可能有用
// 对于WordCount,我们只关心文本内容(map方法的第二个参数)
// 这就是为什么Mapper类通常定义为Mapper<Object, Text, Text, IntWritable>,而不是Mapper<LongWritable, Text, Text, IntWritable>,因为通常不关心这个偏移量的具体类型,只把它视为一个Object。
/* Map阶段核心逻辑:
* 1. 接收<偏移量,行内容>键值对,
* 2. 处理并输出中间结果<单词,1>
* 3. MapReduce框架会收集所有Mapper输出
*/
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
// 输出键值对到Context,将触发Partitioner将数据发送到对应的Reducer
context.write(word, one);
}
// Map任务结束后,框架自动进行Shuffle阶段:
// - Partitioning: 决定数据发送到哪个Reducer
// - Sorting: 按键排序
// - Combining: 可选的本地合并(类似于迷你Reduce)
// - Grouping: 将相同key的值分组
// IntWritable(1) 中的"1"表示单词出现了一次。
// 在WordCount算法中,我们的目标是统计每个单词在文本中出现的总次数。处理过程是:
// Map阶段:每发现一个单词,就输出(单词, 1)的键值对
// 这个"1"就是表示"该单词在当前位置出现了1次"
// 例如处理"Hello World Hello"会输出三对:(Hello, 1), (World, 1), (Hello, 1)
// Reduce阶段:将同一单词的所有计数值(1)累加
// 例如:(Hello, [1,1]) 累加后变成 (Hello, 2)
}
}
// Reducer类实现 - MapReduce第三阶段
// Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>
public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
// reduce方法 - 框架为每个唯一的key调用一次此方法
public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
/* Reduce阶段核心逻辑:
* 1. 接收来自Shuffle阶段的<单词,[1,1,1,...]>
* 2. 聚合计算得到<单词,计数>
* 3. 输出最终结果
*/
int sum = 0;
// 遍历该key的所有值(被框架分组后的)
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
// 输出最终结果到HDFS
context.write(key, result);
}
}
/* 完整作业配置(通常在main方法中):
* Job job = Job.getInstance(conf, "word count");
* job.setJarByClass(WordCount.class);
* job.setMapperClass(TokenizerMapper.class);
* job.setCombinerClass(IntSumReducer.class); // 优化: 使用Combiner减少网络传输
* job.setReducerClass(IntSumReducer.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);
*/
}
优缺点
-
优点:
- 高度容错性
- 适合大规模数据处理
- 编程模型简单
-
缺点:
- 性能较慢(磁盘IO频繁)
- 不适合迭代计算和实时处理
- 编程复杂度高
Hive
-
核心概念
- 构建在Hadoop上的数据仓库工具
- 提供类SQL接口(HiveQL)处理结构化数据
- 自动将SQL转换为MapReduce作业执行
主要组件
-
元数据存储
- 保存表结构、分区等信息
- 通常存储在关系数据库中
-
查询处理器
- 解析HiveQL语句
- 生成执行计划
- 优化查询
-
执行引擎
- MapReduce (传统)
- Tez/Spark (优化)
工作原理简图
[HiveQL查询] → [解析和优化] → [转换为MapReduce] → [执行] → [结果] | | | | [类SQL语法] [生成计划] [物理执行] [返回数据]
适用场景
- 数据仓库
- 报表生成
- 离线数据分析
- 结构化数据查询
HiveQL示例
-- HiveQL示例 - 将SQL语句转换为MapReduce作业执行
/* ===== Hive查询执行流程 =====
* 1. 解析: HiveQL被解析成抽象语法树(AST)
* 2. 语义分析: 验证表、列是否存在,类型检查
* 3. 逻辑计划生成: 转换为操作符树
* 4. 优化: 应用各种规则优化查询计划
* 5. 物理计划生成: 转换为MapReduce/Tez/Spark作业
* 6. 执行: 在集群上运行生成的作业
*/
-- 创建表 - 定义数据结构和存储格式
CREATE TABLE page_views (
user_id STRING, -- 用户ID
timestamp INT, -- 访问时间戳
page_url STRING, -- 页面URL
referrer_url STRING -- 来源URL
)
-- 定义存储格式 - 这会影响Hive如何读取数据
ROW FORMAT DELIMITED -- 行格式为分隔符文本
FIELDS TERMINATED BY '\t'; -- 字段间用制表符分隔
/* 表创建过程:
* 1. 元数据存储: 表定义存入Metastore(通常是MySQL等关系数据库)
* 2. 不会移动或处理实际数据,只创建元数据
*/
-- 分析查询 - 此SQL将被转换为MapReduce作业
SELECT
user_id, -- 输出字段1
COUNT(DISTINCT page_url) AS page_count -- 聚合计算
FROM
page_views -- 输入表
WHERE
timestamp > 1420070400 -- 过滤条件
GROUP BY
user_id -- 分组字段
HAVING
page_count > 10; -- 分组后过滤
/* 查询转换为MapReduce过程:
* 1. Map阶段:
* - 读取表数据并应用WHERE过滤(timestamp > 1420070400)
* - 提取需要的字段(user_id, page_url)
* - 输出键值对<user_id, page_url>
*
* 2. Shuffle:
* - 按user_id分区和排序
* - 相同user_id的所有page_url分组在一起
*
* 3. Reduce阶段:
* - 计算每个user_id下不同page_url的数量
* - 应用HAVING过滤(page_count > 10)
* - 输出最终结果<user_id, page_count>
*
* 注: 复杂查询可能会转换为多个MapReduce作业串联执行
*/
特点
- 适合批处理查询分析
- 支持复杂的ETL流程
- 类SQL语法降低了学习门槛
- 查询延迟较高,不适合低延迟应用
Spark
-
核心概念
- 快速通用的分布式计算引擎
- 内存计算,比MapReduce快10-100倍
- 支持批处理、流处理、机器学习等多种计算
主要组件
-
Spark Core
- RDD (弹性分布式数据集)
- 内存计算框架
- 任务调度
-
生态系统
- Spark SQL:结构化数据处理
- Spark Streaming:实时数据处理
- MLlib:机器学习库
- GraphX:图计算
工作原理简图
[数据源] → [创建RDD] → [转换操作] → [触发动作] → [结果] | | | | [多种来源] [内存缓存] [惰性操作] [实际计算]
【Spark集群架构】
Driver Program (SparkContext)
↓
↙ ↓ ↘
Executor Executor Executor
(内存) (内存) (内存)
tasks tasks tasks
- Driver程序:包含main函数和SparkContext,负责作业调度
- Executor:在工作节点上执行具体任务的进程
- Task:被送到Executor上执行的最小工作单元
### Spark优势
- **速度快**:内存计算
- **易用性**:支持Java、Scala、Python、R
- **统一平台**:一个框架满足多种需求
- **丰富生态**:可无缝对接多种数据源
### 适用场景
- **日志分析**:分析网站访问日志,计算PV/UV
- **推荐系统**:基于用户行为构建推荐模型
- **实时监控**:处理传感器实时数据,检测异常
- **ETL处理**:数据清洗、转换和加载
- **机器学习**:训练预测模型,如客户流失预测
### 优缺点
- **优点**:
- 高性能(内存计算)
- 多语言支持(Java, Scala, Python, R)
- 统一的多种应用场景支持
- 丰富的算子和API
- **缺点**:
- 内存管理复杂
- 调优难度大
- 对内存需求高
### Spark核心编程模型
#### 1. RDD (弹性分布式数据集)
```scala
// 创建RDD
val lines = sc.textFile("hdfs://...")
// 转换操作(Transformation)
val words = lines.flatMap(_.split(" "))
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKey(_ + _)
// 行动操作(Action)
wordCounts.collect()
特点:
- 分区(Partitioned):数据分布在集群多个节点
- 不可变(Immutable):创建后不能修改
- 弹性(Resilient):失败自动恢复
- 延迟计算(Lazy Evaluation):转换操作不立即执行
2. DataFrame和Dataset
// DataFrame示例
val df = spark.read.json("customer.json")
df.filter($"age" > 25).groupBy("city").count()
// Dataset示例
case class Person(name: String, age: Int)
val ds = spark.read.json("people.json").as[Person]
ds.filter(_.age > 25).show()
优势:
- 结构化数据处理
- 优化执行计划
- 与SQL无缝集成
Spark与其他技术对比
【执行速度对比】
MapReduce处理10TB数据: ~1小时30分钟
Spark处理10TB数据: ~10分钟
【迭代计算】
MapReduce: 每次迭代都要读写HDFS
Spark: 中间结果保留在内存中
Spark编程示例
1. WordCount实现对比
// MapReduce实现
public void map(Object key, Text value, Context context) {
StringTokenizer itr = new StringTokenizer(value.toString());
while (itr.hasMoreTokens()) {
word.set(itr.nextToken());
context.write(word, one);
}
}
public void reduce(Text key, Iterable<IntWritable> values, Context context) {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
result.set(sum);
context.write(key, result);
}
// Spark实现
val wordCounts = sc.textFile("hdfs://...")
.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
wordCounts.saveAsTextFile("hdfs://...")
2. Spark SQL查询
// 注册临时表
df.createOrReplaceTempView("people")
// 执行SQL查询
val teenagersDF = spark.sql("SELECT name FROM people WHERE age BETWEEN 13 AND 19")
Spark生态系统
1. 主要组件
- Spark Core:基础计算引擎
- Spark SQL:SQL和结构化数据处理
- Spark Streaming:实时数据处理
- MLlib:机器学习库
- GraphX:图计算引擎
2. 数据源支持
- 文件格式:Text, CSV, JSON, Parquet, ORC, Avro等
- 数据库:HDFS, HBase, MySQL, MongoDB等
- 消息队列:Kafka, Flume等
Flink
基本概念
- 真正的流处理框架,由欧洲研究项目发展而来
- 同时支持批处理和流处理
- 以"无界数据流"为核心设计理念
架构特点
- JobManager:协调任务分配和检查点
- TaskManager:执行任务的工作节点
- 事件时间处理:支持按数据生成时间而非处理时间处理
- 状态管理:支持强一致性的有状态计算
处理模型
- 数据流:所有数据都被视为流
- 窗口操作:可以在流上定义时间窗口
- 检查点机制:保证故障恢复时的一致性
代码示例
// Flink流式WordCount - 实时统计数据流中的词频
/* ===== Flink流处理模型 =====
* 1. 数据源(Source): 从外部系统读取数据流
* 2. 转换(Transformation): 对数据流进行处理
* 3. 接收器(Sink): 将结果写入外部系统
* 4. 执行: 惰性评估,构建执行图后整体优化执行
* 5. 状态管理: 维护计算状态并支持容错
*/
// 创建源(Source) - 从TCP套接字读取文本流
// socketTextStream包含4个参数: 主机名、端口、分隔符、最大尝试次数
DataStream<String> text = env.socketTextStream("localhost", 9999);
// 定义转换操作链
DataStream<Tuple2<String, Integer>> counts = text
// 转换1: flatMap - 将每行文本分割成单词并输出(单词,1)的二元组
.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
/* 流处理特点:
* - 一次处理一个事件(此处是一行文本)
* - 立即产生输出,不等待批次完成
*/
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
// 对每个输入字符串应用分词
for (String word : value.split("\\s")) {
// 收集器输出(类似于生产者-消费者模式)
out.collect(new Tuple2<>(word, 1));
}
}
})
// 转换2: keyBy - 按单词分组,创建KeyedStream
// keyBy与Spark的groupByKey类似,但Flink会维护状态而不是收集所有数据
.keyBy(0) // 按元组第一个字段(单词)分组
// 转换3: sum - 对分组后的流计算滚动总和
// 这涉及有状态计算:
// - 为每个key维护一个状态
// - 每当新事件到来,更新对应key的状态
// - 自动处理状态持久化和恢复
.sum(1); // 累加元组第二个字段(计数)
/* 流式处理区别于批处理:
* 1. 无界数据: 流是无限的,不像批处理有明确的开始和结束
* 2. 实时处理: 数据到达即处理,延迟通常为毫秒级
* 3. 状态管理: 需要维护计算状态(如每个单词当前的计数)
* 4. 容错机制: 通过检查点(Checkpoint)保证精确一次处理语义
*/
// 定义接收器(Sink) - 将结果输出到控制台
// 在生产环境中,通常会输出到Kafka、数据库等外部系统
counts.print(); // print()是一个内置的Sink
/* 执行流程:
* 1. 以上代码只是构建了执行图,没有实际执行
* 2. 调用env.execute()后,Flink才会:
* - 优化执行计划
* - 分配任务到TaskManager
* - 建立数据流通道
* - 开始连续处理数据流
*/
优缺点
-
优点:
- 低延迟、高吞吐
- 精确一次(exactly-once)语义保证
- 强大的时间处理能力
- 有状态计算支持
-
缺点:
- 生态系统相对较新
- 学习曲线较陡
- 调优和运维复杂度高
各技术之间的关系
- Hadoop是基础平台,提供分布式存储(HDFS)和资源管理(YARN)
- MapReduce是Hadoop上的原生计算模型
- Hive构建在Hadoop之上,提供数据仓库功能和SQL接口
- Spark最初也是构建在Hadoop上,但提供了比MapReduce更高效的计算模型
- Flink是独立的流处理框架,但通常也与Hadoop生态集成
实际应用选择
- 需要简单批处理:Hadoop MapReduce
- 需要类SQL分析:Hive
- 需要混合批处理和机器学习:Spark
- 需要低延迟流处理:Flink
- 完整方案通常组合使用多种技术