Spark HadoopRDD读取HDFS文件

本文详细分析了Spark HadoopRDD如何读取HDFS文件,包括HadoopRDD的预分区计算方式、分区划分原理以及可能遇到的特殊情况,如非首个partition可能分不到数据的情况,并提供了源码分析链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前置条件

  • Hadoop版本: Hadoop 2.6.0-cdh5.15.0
  • Spark版本: SPARK 1.6.0-cdh5.15.0

概述

  • 源码分析Spark HadoopRDD是如何读取HDFS上的文件
  • 分析HadoopRDD预分区的计算方式,非首个分区的开始位置计算
  • 来三种情况分析,不同情部下HadoopRDD的分区计算方式

HDFS数据文件

a b k l j
c a n m o

HDFS 数据文件图解

 

HDFS 数据文件图解(对比)

图一

 

图二

 

断点位置

  • org.apache.hadoop.mapred.LineRecordReader 241行, 246行, 248行,136行

HadoopRDD partition预划分方式(实际会有小的调整)

  • 每个partition的长度= 文件的总长度 / 最小的分区数(默认分区数为2) //注意,是除,结果会取整, 即 goalSize = totalSize / numSplits
  • 示例中每个partition的长度 = 20 / 2 =10 // 即为10个byte
  • 然后依次从0开始划分10个byte长度为一个partition,最后一个小于等于10个byte的为最后一个partition
  • 所以 parition(0) = hdfs文件(0 + 10) //即从文件偏移量为0开始,共10byte,0 <= 值 < 10
  • 所以 parition(1) = hdfs文件(10 + 10) //即从文件偏移量为10开始,共10byte,10 <= 值 < 20
  • 即 partition(i) = hdfs文件( i * goalSize + 10 )

HadoopRDD partition划分原理

  • 由于需要考虑,每个partition谁先执行是不确定的,所以每个partition执行时,都需要可明确计算当前partition的数据范围
  • 由于直接按partition预划分方式,会把有的一行数据拆分,有些场景不适合(如钱金额,词组一般都不希望被拆分,所以一般按行拆分)
  • 所以需要按行做为最小的数据划分单元,来进行partition的数据范围划分
  • HadoopRDD是这样划分的partition,还是按partition预划分方式进行预先划分,不过在计算时会进行调整
  • 对于首个partition,也就是partition(0),分区数据范围的开始位置就是从0开始(0 + goalSize )
  • 对于非首个partition,的开始位置需要从新计算,从预划分的当前partition的开始位置开始找第一个换行符位置(indexNewLine),当前partition的开始位置为= indexNewLine + 1,长度还是goalSize
  • 对于首个partition一定能分到数据(只要HDFS文件有数据)
  • 非首个partition,有可能分不到数据的情况,分不到数据的情况,就是数据被上一个partition划分完了

partition分不到数据(以下情况同时满足)

  • 是非首个partition,也就是不是partition为索引为0
  • partition从预分区开始位置往后读到的第一个换行符大于等于预分区的结束位置 (或者该partition就没有一个换行符)

源码分析

 override def compute(theSplit: Partition, context: TaskContext): InterruptibleIterator[(K, V)] = {
    val iter = new NextIterator[(K, V)] {

      val split = theSplit.asInstanceOf[HadoopPartition]
      logInfo("Input split: " + split.inputSplit)
      val jobConf = getJobConf()

      val inputMetrics = context.taskMetrics.getInputMetricsForReadMethod(DataReadMethod.Hadoop)

      // Sets the thread local variable for the file's name
      split.inputSplit.value match {
        case fs: FileSplit => SqlNewHadoopRDDState.setInputFileName(fs.getPath.toString)
        case _ => SqlNewHadoopRDDState.unsetInputFileName()
      }

      // Find a function that will return the FileSystem bytes read by this thread. Do this before
      // creating RecordReader, because RecordReader's constructor might re
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值