实战spark core数据读取&存储

前言

spark sql[spark 1.0.0]出现之前,数据的读取是通过sparkContext得到的是RDD,数据的存储是通过不同类型RDD的saveXXX方法存储的,Spark的整个生态系统与Hadoop是完全兼容的,所以对于Hadoop所支持的文件类型或者数据类型,Spark也同样支持。另外,由于Hadoop的API有新旧两个版本,所以Spark为了能够兼容Hadoop所有的版本,也提供了两套创建操作接口,写入和读取的本质都是通过hadoopFile进行操作的,hadoop的文件格式里面包含了K,V,所有的写入方式最终都是转换为k,v然后写入,所有的读取方式都是先读入K,V然后选取需要的K或V或者K,V来进行。

text/json/csv数据

  1. 这是最常见的数据存储方式,读取path的数据,返回RDD,每一行是一个string,然后就可以用rdd的基本操作进行处理数据,json数据可以通过json库进行解析,csv可以根据分隔符进行切分得到tuple

    // 读取文件,成为RDD的一行
    def textFile(
          path: String,
          minPartitions: Int = defaultMinPartitions): RDD[String]
    
    // 读取文件,返回(K, V), K是文件名
    // 在每个文件表示一个特定时间段内的数据时比较有用
    def wholeTextFiles(
        path: String, minPartitions: Int = defaultMinPartitions): RDD[(String, String)]
    
  2. 存储:存储数据到路径中,具体过程是先转换为key为null的pairRdd,然后调用saveAsHadoopFile写入文件。

    def saveAsTextFile(path: String): Unit
    
  3. 实战

    // 一行数据
    scala> val rdd = sc.textFile("/user/datasource/text")
    rdd: org.apache.spark.rdd.RDD[String] = /user/datasource/text MapPartitionsRDD[1] at textFile at :24
    
    scala> rdd.collect
    res1: Array[String] = Array(null->Michael, 30->Andy, 19->Justin)
    
    // json数据和csv数据可以通过过textFile读取后进行自行解析得到结果
    scala> val rdd = sc.textFile("/user/datasource/json")
    rdd: org.apache.spark.rdd.RDD[String] = /user/datasource/json MapPartitionsRDD[3] at textFile at :24
    
    scala> rdd.collect
    res2: Array[String] = Array({"name":"Michael"}, {"age":30,"name":"Andy"}, {"age":19,"name":"Justin"})
    
    // 存储
    scala> data.saveAsTextFile("/user/datasource/text")
    

Sequence数据

SequenceFile是一个由二进制序列化过的key/value的字节流组成的文本存储文件,它可以在map/reduce过程中的input/output 的format时被使用。在map/reduce过程中,map处理文件的临时输出就是使用SequenceFile处理过的。 所以一般的SequenceFile均是在FileSystem中生成,供map调用的原始文件。

  1. 读取:SequenceFile 是由没有相对关系结构的键值对文件组成的常用 Hadoop 格式。 SequenceFile 也是Hadoop MapReduce 作业中常用的输入输出格式。(即经常见到的Writeable的键值对文件)。

    def sequenceFile[K, V](path: String,  keyClass: Class[K], valueClass: Class[V],
        minPartitions: Int): RDD[(K, V)] 
     
    def sequenceFile[K, V](path: String, keyClass: Class[K], valueClass: Class[V]
          ): RDD[(K, V)]
     
    def sequenceFile[K, V](path: String, minPartitions: Int = defaultMinPartitions)
           (implicit km: ClassTag[K], vm: ClassTag[V],
            kcf: () => WritableConverter[K], vcf: () => WritableConverter[V])
          : RDD[(K, V)] 
    
  2. 存储:用来对类型的RDD进行存储hadoop sequenceFile,为什么不放在PairRDDFunctions中,主要是考虑到这个函数需要额外的转换key和value的参数。

    # org.apache.spark.rdd.SequenceFileRDDFunctions
    def saveAsSequenceFile(
          path: String,
          codec: Option[Class[_ <: CompressionCodec]] = None): Unit
    
  3. 实战

    scala> val data = sc.parallelize(List(("Panda", 3), ("Kay", 6)))
    data: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[10] at parallelize at :24
    
    scala> data.saveAsSequenceFile("/user/datasource/sequence")
    
    
    scala> val rdd = sc.sequenceFile("/user/datasource/sequence", classOf[org.apache.hadoop.io.Text], classOf[org.apache.hadoop.io.IntWritable])
    rdd: org.apache.spark.rdd.RDD[(org.apache.hadoop.io.Text, org.apache.hadoop.io.IntWritable)] = /user/datasource/sequence HadoopRDD[12] at sequenceFile at :24
    

注意: saveAsSequenceFile是SequenceFileRDDFunctions中的函数,不是rdd自身的函数,为什么可以调用,这是因为RDD的伴生对象里面定义了若干的隐式转化方法.

  implicit def rddToSequenceFileRDDFunctions[K, V](rdd: RDD[(K, V)])
      (implicit kt: ClassTag[K], vt: ClassTag[V],
                keyWritableFactory: WritableFactory[K],
                valueWritableFactory: WritableFactory[V])
    : SequenceFileRDDFunctions[K, V] = {
    implicit val keyConverter = keyWritableFactory.convert
    implicit val valueConverter = valueWritableFactory.convert
    new SequenceFileRDDFunctions(rdd,
      keyWritableFactory.writableClass(kt), valueWritableFactory.writableClass(vt))
  }

Object数据

对象数据是对 SequenceFile的简单封装,它允许存储只包含值的 RDD。和SequenceFile 不一样的是,对象文件是使用 Java 序列化写出的。

saveAsObjectFile函数是依赖于saveAsSequenceFile函数实现的,将RDD的key和value转化为Writable的类型,输出RDD作为一个Hadoop SequenceFile,之后调用saveAsHadoopFile函数实现相应的写操作。

def saveAsObjectFile(path: String): Unit = withScope {
    this.mapPartitions(iter => iter.grouped(10).map(_.toArray))
    .map(x => (NullWritable.get(), new BytesWritable(Utils.serialize(x))))
    .saveAsSequenceFile(path)
}

hadoop数据

由于Hadoop的API有新旧两个版本,所以Spark为了能够兼容Hadoop所有的版本,也提供了两套创建操作接口。读取和写入都需要以下四个参数:

  • 输入格式(InputFormat): 数据输入的类型,如TextInputFormat等,新旧两个版本所引用的版本分别是org.apache.hadoop.mapred.InputFormat和org.apache.hadoop.mapreduce.InputFormat(NewInputFormat)
  • 键类型: 指定[K,V]键值对中K的类型
  • 值类型: 指定[K,V]键值对中V的类型
  • 分区值: 指定由外部存储生成的RDD的partition数量的最小值,如果没有指定,系统会使用默认值defaultMinSplits
  1. 读取

    def hadoopFile[K, V](
          path: String,
          inputFormatClass: Class[_ <: InputFormat[K, V]],
          keyClass: Class[K],
          valueClass: Class[V],
          minPartitions: Int = defaultMinPartitions): RDD[(K, V)]
          
    def newAPIHadoopFile[K, V, F <: NewInputFormat[K, V]](
          path: String,
          fClass: Class[F],
          kClass: Class[K],
          vClass: Class[V],
          conf: Configuration = hadoopConfiguration): RDD[(K, V)]    
    
  2. 写入

    def saveAsHadoopFile(
          path: String,
          keyClass: Class[_],
          valueClass: Class[_],
          outputFormatClass: Class[_ <: OutputFormat[_, _]],
          conf: JobConf = new JobConf(self.context.hadoopConfiguration),
          codec: Option[Class[_ <: CompressionCodec]] = None): Unit
    
    def saveAsNewAPIHadoopFile(
          path: String,
          keyClass: Class[_],
          valueClass: Class[_],
          outputFormatClass: Class[_ <: NewOutputFormat[_, _]],
          conf: Configuration = self.context.hadoopConfiguration): Unit
    
  3. 实战

    scala> data.saveAsHadoopFile("/user/datasource/hadoop", 
                                 classOf[org.apache.hadoop.io.Text], 
                                 classOf[org.apache.hadoop.io.IntWritable],                           classOf[org.apache.hadoop.mapred.TextOutputFormat[org.apache.hadoop.io.Text, org.apache.hadoop.io.IntWritable]])
    
    scala> var rdd=sc.hadoopFile("/user/datasource/hadoop",                            classOf[org.apache.hadoop.mapred.KeyValueTextInputFormat], classOf[org.apache.hadoop.io.Text], classOf[org.apache.hadoop.io.Text])
    rdd: org.apache.spark.rdd.RDD[(org.apache.hadoop.io.Text, org.apache.hadoop.io.Text)] = /user/datasource/hadoop HadoopRDD[18] at hadoopFile at :24
    
    scala> rdd.collect
    res17: Array[(org.apache.hadoop.io.Text, org.apache.hadoop.io.Text)] = Array((Panda,3), (Kay,6))
    

参考

  1. https://www.jianshu.com/p/3c2e7501f6f4
  2. https://www.cnblogs.com/husky/p/9178197.html
  3. textfile,sequencefile区别: https://blog.csdn.net/qq_26442553/article/details/80300714
  4. https://blog.csdn.net/yhb315279058/article/details/50466075

你可能感兴趣的:(Spark)