磁盘块管理器DiskBlockManager

简介

DiskBlockManager主要用来创建并持有逻辑blocks块与物理磁盘位置之间的映射。逻辑block块通过其BlockId映射到一个磁盘上的文件。Block块文件被hash存储到spark.local.dir(或SPARK_LOCAL_DIRS)配置的列表目录中。

创建

BlockManager初始化时会创建DiskBlockManager:

val diskBlockManager = {
  // Only perform cleanup if an external service is not serving our shuffle files.
  val deleteFilesOnStop =
    !externalShuffleServiceEnabled || executorId == SparkContext.DRIVER_IDENTIFIER
  new DiskBlockManager(conf, deleteFilesOnStop)
}

主要成员

  • localDirs: Array[File]:创建spark.local.dir配置指定的目录列表。然后在这些目录下创建用来存放中间数据的子目录(为了避免顶层目录的inodes过大),最后会根据文件名将其hash存放到不同的目录下。
  • subDirsPerLocalDir:localDirs代表的各个目录下的子目录的个数,由 spark.diskStore.subDirectories指定,默认为64个。
  • subDirs: Array[Array[File]]:localDirs代表的各个目录下的子目录,子目录个数由 subDirsPerLocalDir指定。subDirs是不可变的,但是其每个成员subDirs(i)是可变的,并且subDirs(i)都是被subDirs(i)的锁保护的。
  • shutdownHook:即关闭钩子,在进程结束时会递归删除localDirs下所有属于该Application的文件。

主要功能

DiskBlockManager管理了每个BlockId到磁盘上文件的映射关系。DiskBlockManager主要有如下几个功能:

  • 创建本地目录;
  • 获取BlockId对应的文件路径;
  • 查询BlockId对应的文件是否存在;
  • 创建临时Block文件;

创建本地目录

负责创建配置的本地节点上的指定列表磁盘目录,用来存储Block数据到指定文件中。当使用外部shuffle服务时,在JVM退出时目录不会被删除。

private def createLocalDirs(conf: SparkConf): Array[File] = {
  // 获取配置的本地目录列表
  Utils.getConfiguredLocalDirs(conf).flatMap { rootDir =>
    try {
	  // 创建目录,目录名为rootDir + "blockmgr-" + UUID.randomUUID.toString
      val localDir = Utils.createDirectory(rootDir, "blockmgr")
      logInfo(s"Created local directory at $localDir")
      Some(localDir)
    } catch {
      case e: IOException =>
        logError(s"Failed to create local dir in $rootDir. Ignoring this directory.", e)
        None
    }
  }
}

获取BlockId对应的文件路径

如果Block数据想要落盘,首先需要通过调用getFile方法来分配一个唯一的文件路径(寻找该blockId对应文件也是通过这种方式)。其处理步骤如下:

  • 根据文件名计算哈希值;

  • 根据哈希值与本地文件一级目录的总数求余数,记为dirId;

  • 根据哈希值与本地文件一级目录的总数求商数,此商数与二级目录的数目再求余数,记为subDirId;

  • 如果dirId/subDirId目录存在,则获取dirId/subDirId目录下的文件,否则新建dirId/subDirId目录。

      def getFile(blockId: BlockId): File = getFile(blockId.name)
    
      // 通过将文件名hash到本地子目录的方式来查找文件
      // 该方法应该与"org.apache.spark.network.shuffle.ExternalShuffleBlockResolver#getFile()"方法保持同步
      def getFile(filename: String): File = {
        // 得到该文件名hash到的本地目录,以及该目录下的子目录
        val hash = Utils.nonNegativeHash(filename)
        val dirId = hash % localDirs.length
        val subDirId = (hash / localDirs.length) % subDirsPerLocalDir
      
        // 如果目录不存在则创建该子目录
        val subDir = subDirs(dirId).synchronized {
          val old = subDirs(dirId)(subDirId)
          if (old != null) {
            old
          } else {
            val newDir = new File(localDirs(dirId), "%02x".format(subDirId))
            if (!newDir.exists() && !newDir.mkdir()) {
              throw new IOException(s"Failed to create local dir in $newDir.")
            }
            subDirs(dirId)(subDirId) = newDir
            newDir
          }
        }
      
        new File(subDir, filename)
      }
    

查询BlockId对应的文件是否存在

查询BlockId对应的文件是否存在,也是通过获取BlockId对应的文件路径是否存在来实现的。

/** Check if disk block manager has a block. */
def containsBlock(blockId: BlockId): Boolean = {
  getFile(blockId.name).exists()
}

创建临时Block文件

当ShuffleMapTask运行结束需要把中间结果临时保存,此时就调用createTempShuffleBlock方法创建临时的Block文件,并返回TempShuffleBlockId(TempShuffleBlockId的生成规则:"temp_shuffle_"后加上UUID字符串)与其对应的文件。

/** Produces a unique block id and File suitable for storing shuffled intermediate results. */
def createTempShuffleBlock(): (TempShuffleBlockId, File) = {
  var blockId = new TempShuffleBlockId(UUID.randomUUID())
  while (getFile(blockId).exists()) {
    blockId = new TempShuffleBlockId(UUID.randomUUID())
  }
  (blockId, getFile(blockId))
}

/** Id associated with temporary shuffle data managed as blocks. Not serializable. */
private[spark] case class TempShuffleBlockId(id: UUID) extends BlockId {
  override def name: String = "temp_shuffle_" + id
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值