当前位置: 首页 > article >正文

Spark的Torrent Broadcast 详解

        在Spark中,Broadcast是一种用于分发较大的只读数据的机制,目的是避免在集群中的每个节点重复传输相同的数据。通过Broadcast机制,Spark可以将这些数据高效地发送到每个工作节点上,从而降低数据传输的成本并提升作业的性能。

        其中,Torrent Broadcast 是Spark中用于优化广播变量分发的一种实现。它利用了一种类似于P2P文件共享(如BitTorrent)的方式,在集群中以块的形式逐步扩散数据。这种方式与传统的直接广播不同,后者通常由驱动程序将整个数据直接传输到每个工作节点。Torrent Broadcast可以通过逐步扩散的方式大幅减少网络带宽的使用。

1. 广播机制的背景

        在分布式系统中,工作节点(executor)之间经常需要共享一些只读数据,这些数据通常不会频繁改变。例如,机器学习中的模型参数、大型查找表等。如果没有有效的广播机制,每个任务可能需要从驱动程序重复获取这些数据,造成网络瓶颈。为了提高效率,Spark引入了广播变量(Broadcast Variable),其目的是使得集群中的每个工作节点可以高效地访问相同的数据。

2. Spark的Broadcast工作原理

Spark中的Broadcast机制的基本流程如下:

  • 序列化数据:首先,广播的只读数据会被序列化为二进制形式。
  • 分块:为了使数据更高效地传输,Spark将广播数据分成多个小块。
  • 逐步传播:通过分块传输,Torrent Broadcast的工作原理类似于BitTorrent协议——不是由单个驱动程序直接将数据发送到所有工作节点,而是通过分块让接收节点充当数据的“中继器”,将自己接收到的部分数据继续广播给其他节点。每个节点只需从少量节点获取其缺少的块。

通过这种方式,集群中的工作节点可以以分散的方式共享数据,极大降低了网络的负载。

3. Torrent Broadcast的实现原理

Torrent Broadcast的核心在于分块传输与逐步扩散的机制。它通过以下步骤进行:

  1. 数据序列化与分块

    • 广播数据首先被序列化为字节数组,然后被分成若干固定大小的块(block)。Spark默认将广播数据的每个块大小设置为4MB。
    • 这些块会存储在磁盘上,并通过BlockManager管理。
  2. 数据块传播

    • 当某个Executor需要广播数据时,它首先尝试从本地(如缓存或磁盘)读取数据。
    • 如果本地不存在所需的块,则它会向Driver请求数据的某一部分。
    • Driver不会将所有数据一次性发送给请求的Executor,而是将数据块发送给不同的Executor。然后,已经获得部分数据块的Executor会继续充当其他Executor的中转节点,从而将数据块传递给更多的节点。这类似于BitTorrent协议中的“种子”节点概念。
  3. 节点之间的P2P传输

    • 各个节点接收到一部分数据块后,会将其保存在自己的本地磁盘或内存中,然后充当其他Executor的源节点。需要数据的其他Executor可以从这些拥有部分数据的节点请求数据块。
    • 这样,通过这种块级别的P2P传输机制,Driver只需发送数据到少数几个节点,后续节点可以通过相互通信来获取数据块。
  4. 容错机制

         如果某个节点在接收数据块的过程中失败,Spark的BlockManager可以重新从其他节点请求数据块,确保广播操作的容错性。

4. Torrent Broadcast的具体实现细节

从代码层面,Torrent Broadcast的主要实现位于以下几个类中:

  1. TorrentBroadcast

           这是Spark中实现广播机制的核心类。它负责将要广播的变量序列化并分块,然后通过BlockManager分发到集群的各个节点。

    关键方法:

  • writeBlocks: 该方法将数据切分为块并将这些块存储到BlockManager中,驱动和Executor都会调用这个方法。
  • readBlocks: 该方法负责从BlockManager中读取块。如果块不在本地,会从其他节点或驱动中获取。
  1. BlockManager

    关键方法:

    • 负责管理块的存储和分发。数据块在本地磁盘或内存中缓存,并通过这个组件分发到其他节点。
    • putBlock: 将块存储到本地的内存或磁盘。
    • getBlock: 从本地获取块,如果本地没有,则从远程节点获取。
  2. BroadcastManager

            这是一个抽象类,TorrentBroadcast继承并实现了这个类的功能,提供不同的广播实现方式。

5. 源码解读

下面简要说明TorrentBroadcast的部分关键代码逻辑。

序列化与分块

private def writeBlocks(value: T, blockManager: BlockManager, broadcastId: Long): Unit = {
  val blocks = TorrentBroadcast.blockifyObject(value, blockSize)
  blocks.zipWithIndex.foreach { case (block, i) =>
    blockManager.putSingle(broadcastBlockId(broadcastId, i), block, StorageLevel.MEMORY_AND_DISK, tellMaster = false)
  }
}

        这里调用blockifyObject方法将value序列化并切分为多个块,然后通过blockManager将这些块存储到内存和磁盘中。

获取广播数据

private def readBlocks(): Array[ByteBuffer] = {
  val numBlocks = numBlocksForBroadcast()
  val blocks = new Array[ByteBuffer](numBlocks)
  for (i <- 0 until numBlocks) {
    val blockId = broadcastBlockId(broadcastId, i)
    blocks(i) = blockManager.getBlockData(blockId).nioByteBuffer()
  }
  blocks
}

        readBlocks方法负责从BlockManager中读取广播的块。如果本地没有找到所需的块,会从其他节点拉取。

6. Torrent Broadcast的优势

相比传统的直接广播,Torrent Broadcast有以下几个优点:

  1. 减少网络瓶颈:通过P2P的传输模式,Driver不再需要直接将数据发送给每个Executor,极大减少了Driver的网络负担。
  2. 高效的带宽利用:各个节点在完成部分数据接收后会充当中继节点,极大提高了带宽的利用率。
  3. 容错性强:由于数据是分块存储并传播的,如果某个节点失效,其他节点仍然可以从其他节点获取缺失的数据块。

7.实现上面优势所带来的代价

7.1. 实现复杂性和调度开销
  • 复杂的数据分发管理:Torrent Broadcast需要管理每个数据块的分发情况,并确保所有工作节点能够获取完整的广播数据。这意味着需要在多个Executor之间协调块的传输,并在失效时重新调度数据传输路径。这种机制比直接广播增加了实现的复杂性。
  • 额外的调度开销:每个节点不仅要处理自己的任务,还可能需要充当其他节点的数据传输“中继站”,这增加了调度的复杂度。任务调度器需要确保任务运行期间不会发生数据丢失或数据传输阻塞,这会在系统中引入额外的调度开销。
7.2. 块管理的存储开销(不是很重要)
  • 内存与磁盘存储开销:为了实现分块传输,数据需要被切分为多个较小的块。每个节点在接收到数据块后,会将这些块缓存到本地的内存或磁盘中,这会占用系统资源。特别是当广播数据非常大时,缓存多个数据块可能会占用较多的内存和磁盘空间。
  • 块索引和元数据管理开销:Torrent Broadcast需要维护每个块的元数据,包括块的索引、位置和状态。虽然块本身可能不大,但这些元数据也会引入一定的存储和管理开销。
7.3. 额外的网络开销
  • 初始传播的多轮通信:尽管最终可以减少网络带宽的整体消耗,但由于广播数据以块为单位逐步传输,这意味着某些数据块的传输可能需要经过多次中转,造成多轮通信。因此,在广播的初期阶段,网络开销可能会暂时增大,特别是当Executor之间的通信开销较高时。
  • 节点间的多次请求:每个节点需要通过从多个其他节点获取缺失的数据块,这会导致额外的网络请求。在某些情况下,多个节点同时从同一个节点获取数据块,可能会导致负载不均衡或局部网络拥塞
7.4. 数据块传输的延迟
  • 数据块分布不均可能导致延迟:由于数据块在集群中逐步扩散,某些节点可能会因为其他节点传输数据块的速度较慢而出现延迟。如果某个节点在获取所有块之前需要等待其他节点传输数据,则任务的执行可能会被延迟。
  • 中继节点带来的传输延迟:每个中继节点在接收到数据后,需要将数据传给其他节点,这个中继过程引入了额外的延迟,尤其是在网络速度较慢或中继节点的资源有限时。
7.5. 复杂的容错处理
  • 数据重传和恢复开销:如果某个节点在接收过程中出现故障,Torrent Broadcast需要重新从其他节点或Driver获取丢失的数据块。这种容错机制尽管提升了可靠性,但也引入了复杂的错误恢复机制。当集群中节点失效率较高时,容错机制可能会频繁触发,导致重传开销增加。
  • 冗余通信和存储:为了保证数据的可靠传输,Torrent Broadcast有可能在多个节点上存储冗余数据块,导致存储资源的浪费。在节点失效的情况下,重新请求数据块的过程也会增加网络通信开销。
7.6. 内存与CPU资源的额外消耗
  • 多任务环境下的竞争:由于各个节点需要负责不仅仅是自身任务,还可能充当其他节点的数据传输节点,这会引发额外的CPU负载和内存占用。如果广播数据块较多,某些节点可能会面临资源竞争问题,导致任务执行的延迟或性能下降。
  • 序列化与反序列化开销:每个数据块的序列化与反序列化过程会占用额外的CPU资源。在广播变量较大时,这种开销可能会显著增加,尤其是在数据频繁被存储到磁盘或从磁盘中读取的场景下。
7.7. 局部热点问题
  • 节点负载不均衡:尽管Torrent Broadcast分散了数据块的传输,但由于节点的硬件性能和网络连接情况不同,某些节点可能成为“热点”,需要频繁传输数据块。这种不均衡的情况可能会影响广播过程的效率,导致个别节点的负载明显高于其他节点。

总结

        Spark的Torrent Broadcast是为了优化大数据分发而设计的一种高效广播机制。它采用类似BitTorrent的分布式数据传播方式,通过分块传输和逐步扩散,将广播数据分发到各个工作节点。相比传统的广播模式,Torrent Broadcast能够更好地利用网络带宽,并提供更好的容错性和扩展性。


http://www.kler.cn/a/369236.html

相关文章:

  • git bisect和git blame
  • JavaScript 前端开发
  • 【C++进阶】C++11(上)
  • CSS中为特定的元素设置背景图片(Top1,Top1,Top3)
  • STM32L476芯片在KEIL环境下BOOT跳转APP注意事项
  • Maven:详解 clean 和 install 命令的使用
  • 基于YOLOv11的动物类别实时检测系统(python+pyside6界面+系统源码+可训练的数据集+也完成的训练模型)
  • Python 基础语法 - 赋值运算符
  • 无人机之倾斜摄影技术篇
  • 【代码随想录Day53】图论Part05
  • 海外服务器的价格取决于服务器的性能和租赁时间
  • leetcode-73-矩阵置零
  • 【LeetCode】每日一题 2024_10_22 构成整天的下标对数目 I(暴力/哈希)
  • Golang | Leetcode Golang题解之第502题IPO
  • 嵌入式1_ARM学习(六)——Makefile
  • 【GPIO】2.ADC配置错误,还是能得到电压数据
  • CRC 校验码
  • 【iOS】知乎日报第一周总结
  • Vue3_开启全局websocket
  • Qt6切换音轨
  • ffmpeg视频滤镜:均值模糊-boxblur
  • MAN Truck Bus EDI 需求分析
  • Flutter Column组件实战案例
  • 2024 最新 frida技术栈 第一部分
  • Linux云服务器安装Docker、MySQL、Redis
  • 国产系统安装Oracle报错处理