HadoopMapReduce和Spark究竟存在什么差异

首先是架构对比:

Hadoop MapReduce是一个两阶段模型,它强制分为Map和Reduce阶段:

1.在Map阶段中,它执行的操作是:读取HDFS数据 ----->本地计算------>写本地磁盘

2.在Shuffle阶段:通过网络传输排序数据

3.在Reduce阶段:拉取数据 ---->聚合计算------>写HDFS

注:HDFS全程Hadoop Distributed File System,它是专门为大数据存储设计的分布式文件系统,它设计的目标是有三个,1是超大规模存储,它的单个集群可扩展到PB级 2.具有高容错性,硬件的故障视为常态而非异常,3.流式数据访问:优化数据吞吐量而非低延迟(适合批量处理而非实时访问)

它有一个数据流缺陷的问题,即每个阶段结束后数据必须落盘(磁盘IO成为瓶颈)

接下来来了解一下HadoopMapReduce的内部结构:

NameNode是主节点,它管理文件系统命名空间(文件名,目录结构,权限),它还会记录文件块的映射(Block到DataNodde的映射)

DataNode是工作节点,它实际存储数据块(默认128MB/块),定期向NameNode发送心跳(默认3秒)和块报告,并且数据块会自动复制

数据存储机制:

从客户端的视角出发,文件写入的过程是:

1.客户端联系NameNode获取可写DataNode列表

2.建立数据通道:Client -> DataNode1 ->DataNode2 ->DataNode3(默认3副本)

3.数据以包(Packet,默认64KB)为单位传输

4.确认信息通过反向管道返回客户端

高可用方案(HA)

  • 双NameNode架构

    • Active NameNode:处理客户端请求

    • Standby NameNode:同步EditLog,准备故障接管

    • 使用ZooKeeper实现自动故障转移(Failover时间<30秒)

它支持与Spark的集成 :

典型的数据流

在企业中,我们都会使用Parquet/ORC列式存储格式,避免使用TextFile文件格式,可以节省50%的存储空间,然后HDFS块大小和Spark分区数的关系最好是1:1

Spark核心部分:DAG引擎构建的过程:

 

  • 阶段划分规则

    • 窄依赖

      (Narrow Dependency):父RDD的每个分区最多被子RDD的一个分区使用

      • 示例:map、filter、flatMap等转换操作

      • 优化机会:多个窄依赖操作合并为单个Stage

    • 宽依赖

      (Wide Dependency):父RDD的分区被多个子RDD分区使用(Shuffle产生)

      • 示例:reduceByKey、join、repartition等操作

      • 触发Stage边界划分

DAG内存管理架构:

参数解释:

  1. ExecutorMemory(执行器内存)

    • Spark 执行器进程的总内存

    • 默认由 spark.executor.memory 配置指定

  2. StorageMemory(存储内存)- 60%

    • 用于缓存 RDD、广播变量和数据帧

    • 当内存不足时使用 LRU(最近最少使用)策略淘汰数据

    • 可通过 spark.memory.storageFraction 调整比例

  3. ExecutionMemory(执行内存)- 40%

    • 用于存储 Shuffle、Join、Sort 等计算过程中的临时数据

    • 在多个任务之间共享

    • 剩余比例 = 1 - storageFraction

  4. DiskSpill(磁盘溢出)

    • 当 StorageMemory 空间不足时

    • 使用 LRU 策略将最近最少使用的数据块溢写到磁盘

  5. ShuffleBuffer(Shuffle缓冲区)

    • 在 Task 之间共享的执行内存空间

    • 用于 Shuffle 操作的数据缓存

    • 当缓冲区满时会触发溢写到磁盘

3. 与MapReduce的对比优势

执行模型对比
阶段MapReduceSpark
任务调度每个Task独立JVM进程线程池复用(毫秒级启动)
数据传递必须经过磁盘内存优先,可选磁盘
中间结果多轮磁盘IO内存缓存复用
容错机制重新执行整个Task基于RDD血统局部重算

什么是RDD:

RDD(Resilient Distributed Dataset)是Spark的核心抽象,代表一个不可变、可分区的分布式数据集合


1. RDD设计哲学

产生背景
  • MapReduce的缺陷

    • 中间数据必须写磁盘(迭代算法效率低下)

    • 缺乏高效的数据复用机制

    • 编程模型不够灵活

  • RDD创新点

    • 内存计算:中间结果可缓存

    • 血统(Lineage)机制:通过记录转换历史实现容错

    • 丰富的操作API:支持超过20种转换操作


2. RDD核心特性

五大核心特征
  1. 分区列表(Partitions):

    val partitions: Array[Partition] = rdd.partitions
    • 数据被划分为多个分区(Partition)

    • 每个分区对应一个Task

    • 可通过repartition()调整分区数

  2. 依赖关系(Dependencies):

    val deps: Seq[Dependency[_]] = rdd.dependencies
    • 窄依赖(Narrow):父RDD的每个分区最多被一个子分区使用

    • 宽依赖(Wide):父RDD的分区被多个子分区使用(触发Shuffle)

  3. 计算函数(Compute Function):

    def compute(split: Partition, context: TaskContext): Iterator[T]
    • 定义如何从父RDD计算当前分区的数据

    • 支持链式转换操作(如map(transformA).map(transformB)

  4. 分区器(Partitioner):

    val partitioner: Option[Partitioner] = rdd.partitioner
    • 决定数据如何分布(HashPartitioner、RangePartitioner)

    • 影响Shuffle效率的关键因素

  5. 首选位置(Preferred Locations):

    def getPreferredLocations(split: Partition): Seq[String]
    • 实现数据本地性(Data Locality)

    • 例如HDFS块的位置信息


3. RDD生命周期

创建到执行流程
容错机制
  • 血统(Lineage)恢复

    val rdd = sc.textFile("hdfs://data")
      .map(parse)          // Lineage记录1
      .filter(_.isValid)   // Lineage记录2
      .cache()
    • 丢失缓存数据时,根据血统重新计算

    • 通过checkpoint()切断过长血统链


4. RDD操作类型

转换(Transformations)
操作类型示例特点
窄依赖转换map(), filter()不触发Shuffle
宽依赖转换reduceByKey(), join()触发Shuffle
重分区repartition(), coalesce()改变分区数
动作(Actions)
操作类型示例输出形式
数据收集collect(), take(N)返回Driver程序
数据存储saveAsTextFile()写入外部存储
聚合计算count(), reduce()返回标量值

5. 企业级应用模式

缓存策略选择
// 根据数据使用频率选择存储级别
if (rdd会被多次使用) {
  rdd.persist(StorageLevel.MEMORY_ONLY_SER) 
} else if (rdd太大无法完全放入内存) {
  rdd.persist(StorageLevel.MEMORY_AND_DISK)
}
性能调优技巧
  1. 分区数优化

    // 合理设置分区数(建议每个分区128MB)
    val optimalPartitions = (dataSize / 128MB).toInt
    rdd.repartition(optimalPartitions)
  2. 避免数据倾斜

    // 对倾斜Key添加随机前缀
    val skewedRdd = rdd.map(k => (s"${Random.nextInt(10)}_$k", v))
  3. 广播变量优化Shuffle

    val smallTable = sc.broadcast(lookupData)
    largeRdd.map { case (k, v) => 
      (smallTable.value.getOrElse(k, default), v)
    }

6. RDD与DataFrame对比

特性RDDDataFrame
数据类型任意对象结构化数据(Row类型)
优化方式开发者手动优化Catalyst自动优化
内存使用较高(Java对象开销)较低(Tungsten二进制格式)
API类型函数式编程SQL风格
序列化Java序列化高效编码

7. 适用场景

推荐使用RDD的场景

  • 非结构化数据处理(如文本、图像)

  • 需要精细控制分区的场景

  • 实现自定义的复杂算法

推荐使用DataFrame的场景

  • 结构化数据分析(类似SQL查询)

  • 需要自动优化的场景

  • 与其他大数据组件集成(如Hive)

2. Spark和Hadoop的MapReduce的性能差异:

基准测试对比
任务类型MapReduce耗时Spark耗时优势倍数
迭代算法(PageRank)120分钟8分钟15x
日志分析(TB级)45分钟6分钟7.5x
机器学习(K-means)90分钟11分钟8x
性能差异根源
  • 磁盘IO:MapReduce每个阶段写磁盘 vs Spark内存计算

  • 任务启动:MapReduce每个Task启动JVM(秒级) vs Spark复用线程池(毫秒级)

  • 数据交换:MapReduce必须Shuffle vs Spark可优化窄依赖操作

4. 生态系统对比

组件Hadoop MapReduceSpark
SQL引擎需依赖Hive(启动MR任务)原生Spark SQL(直接执行)
机器学习需Mahout库(效率低)原生MLlib(内存计算)
流处理无(需Storm等外部系统)结构化流(微批/连续模式)
图计算无(需Giraph)GraphX(集成RDD)

5. 容错机制对比

  • MapReduce

    • Task失败时重新调度(数据从HDFS重新读取)

    • 可靠性高但恢复速度慢(需重新计算整个Task)

  • Spark

    • 基于RDD血统(Lineage)的弹性恢复

    • 丢失RDD分区时,只需根据DAG重新计算父分区

    • Checkpoint机制可切断过长血统链

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值