Spark数据存储等学习

Spark数据读取

  • 对于存储在本地文件系统或分布式文件系统(HDFS、Amazon S3)中的数据,Spark可以访问很多种不同的文件格式,比如文本文件、JSON、SequenceFile
  • Spark SQL中的结构化数据源,包括JSON和Hive的结构化数据源
  • 数据库和键值存储,自带的库,联结HBase或其他JDBC源
格式名称 结构化 备注
文本文件 普通的文本文件,每行一条记录
JSON 半结构化 每行一条记录
CSV 非常常见的基于文本的格式
SequenceFiles 用于键值对的常见Hadoop文件格式

textFile()和saveAsTextFile(),读取文本文件和保存为文本文件。

读取JSON数据的方式是将数据作为文本文件读取,然后使用JSON解析器对RDD中的值进行映射操作。

import json
data = input.map(lambda x: json.loads(x))

//保存JSON
(data.filter(lambda x: x["lovesPands"]).map(lambda x: json.dumps(x)).saveAsTextFile(outputFile))

Spark有专门用来读取SequenceFile的接口,可以调用sequenceFile(path,keyClass,valueClass,minparttions)来读取。

val data = sc.sequenceFile(inFile,"org.apache.hadoop.io.Text","org.apache.hadoop.io.IntWritable")

文件压缩

对数据进行压缩以节省存储空间和网络传输开销。Spark原生的输入方式(texeFile和sequenceFile)可以自动处理一类型的压缩。

文件系统

数据库

Java数据库连接,需要构建一个org.apache.spark.rdd.jdbcRDD,将SparkContext和其他参数一起传给它

//Scala
def createConnect() = {
    Class.forName("com.mysql.jdbc.Driver").newInstance();
    DriverManager.getConnection("jdbc:mysql://localhost/test?user=holden");
}
def extractValues(r: ResultSet) = {
    (r.getInt(1),r.getString(2))
}

val data = new JdbcRDD(sc,createConnection,"SELECT * FROM panda WHERE ",lowerBound = 1, upperBound = 3, numPartitions = 2, mapRow = extractValues)
println(data.collect().toList)
  • 提供一个用于数据库连接的函数
  • 提供一个可以读取一定范围内数据的查询,以及查询参数中lowerBound和upperBound的值。
  • 最后一个参数可以将输出结果从java.sql.ResultSet转为对操作数据有用的格式的函数。

Spark可以用org.apache.hadoop.hbase.mapreduce.TableInputFormat类通过Hadoop输入格式访问HBase。键的类型为org.apache.hadoop.hbase.io.ImmutableBytesWritable,值的类型为org.apache.hadoop.hbase.client.Result。

//Scala
import org.apache.hadoop.hbase.HBaseConfigration
import org.apache.hadoop.hbase.client.Result
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableInputFormat

val conf = HBaseConfiguration.create()
conf.set(TableInputFormat.INPUT_TABLE,"tablename")

val rdd = sc.newAPIHadoopRDD(conf, classOf[TableInputFormat],classOf[ImmutableBytesWritable],classOf[Result])

Spark编程进阶

累加器用来对信息进行聚合,广播变量用来高效分发较大的对象。

累加器的一个常见用途是在调试时对作业执行过程中的事件进行计数。

  • 通过在驱动器调用SparkContext.accumulator(initialValue)方法,创建出有初始值的累加器,返回值为org.apache.spark.Accumulator对象
  • Spark闭包里的执行器代码可以使用累加器的+=方法增加累加器的值
  • 可以调用累加器的value属性来访问累加器的值
  • 工作节点上的任务不能访问累加器的值

广播变量可以高效向所有节点发送一个较大的只读值。

siggPrefixes = sc.broadcast(loadCall())

分区操作

打开数据库连接或创建随机数生成器等操作,我们应避免为每个元素都配置一次的工作。Spark提供基于分区的map和foreach,让你的代码只对RDD的每个分区运行一次。

可以在每个分区共享一个数据库连接池,避免建立太多连接

def processCallsigns(signs):
    http = urllib3.PoolManager()//建立连接池
    urls = map()//操作
    ···

Spark集群上的运行

Spark集群采用的是主/从结构,主是驱动器节点driver,其他是工作节点executor,之间通过集群管理器来连接。

驱动器在Spark应用有两个职责:

  • 把用户程序转为任务。转为多个物理执行的单元,即任务task。驱动器会按DAG图来执行,也会对DAG进行流水线优化,形成一系列stage。每个stage分发给执行器。
  • 为执行器节点调度任务。执行器进程启动后,会向驱动器注册自己,Spark驱动器根据当前的执行器节点集合,把任务基于数据的位置分配给合适的执行器。

执行器进程两大作用:负责运行组成Spark应用的任务,并把结果返还驱动器进程;通过自身的块管理器(Block Manager)为用户程序中要求缓存的RDD提供内存存储,进行内存计算。

  • 用户通过spark-submit脚本提交应用
  • spark-submit脚本启动驱动器程序,调用用户定义的main()方法
  • 驱动器程序与集群管理器通信,申请资源以启动执行器节点
  • 集群管理器为驱动器程序启动执行器节点
  • 驱动器进程执行用户应用中的操作。根据程序中所定义的对RDD的转化操作和行动操作,驱动器节点把工作以任务的形式发送到执行器进程。
  • 任务在执行器程序中进行计算并保存结果
  • 如果驱动器程序的main方法推出,驱动器程序会终止执行器进程,通过集群管理器释放资源

Spark优化

当调用行动操作,会向上回溯,访问父节点,父节点的父节点,然后执行转化操作。

Spark提供了两种方法来对操作的并行度进行调优,第一种方法是在数据混洗操作时,使用参数的方式为混洗后的RDD指定并行度,第二种方法时对于任务已有的RDD,可以进行重新分区来获取更多或更少的分区数。

当Spark需要通过网络传输数据,或将数据溢写到磁盘上,就需要数据序列化为二进制格式。序列化会在混洗操作时发生。

当数据混洗操作时,Spark会创建出一些中间缓存区来存储数据混洗的部分缓存数据。用户也可以申请内存空间。默认情况下60%内存空间给RDD,20%给数据混洗,20%给用户。

如果分区空间不够,旧分区会被删除或写入磁盘。

猜你喜欢

转载自www.cnblogs.com/chenshaowei/p/12425488.html