Hadoop输入输出格式
1、读取其他Hadoop输入格式
要使用新版的Hadoop API读入一个文件,需要告诉Spark一些东西,使用newAPIHadoopFile接受一个路径以及三个类。
第一个类是输入格式。相似的函数hadoopFile()使用旧的API实验的Hadoop输入格式。
第二个类是键的类
最后一个类是值的类。如果需要设定额外的Hadoop配置属性,也可以传入一个conf对象。
KeyValueTextInputFormat可以用于从文本文件中读取键值对数据,每一行都会被独立处理,键和值之间用制表符隔开。
//在Scala中使用老式API读取KeyValueTextInputFormat()
val input = sc.hadoopFile[Text, Text, KeyValueTextInputFormat](inputFile).map{
case (x, y) => (x.toString, y.toString)
}
//在Scala中使用Elephant Bird读取LZO算法压缩的JSON文件
val input = sc.newAPIHadoopFile(inputFile, classOf[LzoJsonInputFormat],
classOf[LongWritable], classOf[MapWritable], conf)
// "输入"中的每个MapWritable代表一个JSON对象
使用旧的Hadoop API读取文件在用法上几乎一样,除了需要提供旧式InputFormat类,Spark许多自带的封装好的函数都是使用旧式Hadoop API实现的
2、保存Hadoop输出格式
3、非文件系统数据源
Spark还可以使用hadoopDataset/saveAsHadoopDataSet和newAPIHadoopDataSet/saveAsNewAPIHadoopDataset来访问Hadoop所支持的非文件系统存储格式。
hadoopDataset()这一组函数只接受一个Configuration对象,该对象用来设置访问数据源所必须的Hadoop属性。你要使用与配置Hadoop MapReduce作业相同的方式来配置这个对象。
文件压缩
对于支持压缩的Hadoop格式,Spark原生的输入方式,可以自动处理一些类型的压缩;在读取压缩后的的数据集时,一些压缩编码器可以推测压缩类型。
写入数据库的Hadoop格式,一般没有实现压缩支持,如果数据库中有压缩过的记录,则是数据库自己配置的。
有些压缩格式,无法实现在每个工作节点都在找到一个新的记录的开端,从而显示了并行读取的效率,造成性能瓶颈。
格式 | 可分割 | 平均压缩速度 | 压缩效率 | Hadoop压缩编解码器 | 纯Java实现 | 原生 | 备注 |
---|---|---|---|---|---|---|---|
gzip | 否 | 快 | 高 | org.apache.hadoop.io.compres.GzipCodec | 是 | 是 | |
lzo | 是(取决于所用的库) | 非常快 | 中等 | com.hadoop.compression.lzo.LzoCodec | 是 | 是 | 需要在每个节点桑安装LZO |
bzip2 | 是 | 慢 | 非常高 | org.apache.hadoop.io.comporess.BzipCodec | 是 | 为可分版本使用出Java | |
zlib | 否 | 慢 | 中等 | org.apache.hadoop.io.compress.DefaultCodec | 是 | 是 | Hadoop的默认压缩编码器 |
Snappy | 否 | 非常快 | 低 | org.apache.hadoop.io.compress.SnappyCodec | 否 | 是 | Snappy有纯java的移植版,但是在Spark/Hadoop中,不能用 |
如需读取单个压缩过的输入,使用newAPIHadoopFile或者hadoopFile,并制定正确的压缩编解码器。
有些输入格式(如SequenceFile)允许我们只压缩简直对中的值,这种形式有利于查询。
Spark SQL中的结构化数据
在各种情况下,我们把一条SQL查询交给Spark SQL,得到由ROW对象组成的RDD,每个Row对象表示一条街记录,在Scala和Java中,ROW对象的访问是基于下标的,每个ROW都有一个get()方法,会返回一个一般类型让我们进行类型转换。另外有专用的get()方法(如getFloat,getInt,getLong(),getString(),getShort())
Apache Hive
如果要把Spark SQL连接到已有的Hive上,需要提供Hive的配置文件,将hive-site.xml文件复制到Spark的./conf/目录下,然后创建出HiveContext对象,然后可以使用HQL(Hive查询语言)进行查询,并返回RDD
//用Scala创建HiveContext并查询
import org.apache.spark.sql.hive.HiveContext
val hiveCtx = new org.apache.spark.sql.hive.HiveContext(sc)
val rows = hiveCtx.sql("SELECT name, age FROM users")
val firstRow = rows.first()
println(firstRow.getString(0)) // 字段0是name字段
JSON数据
要读取JSON数据,需要使用Hive创建HiveContext。
//在Scala中使用Spark SQL读取JSON数据
val tweets = hiveCtx.jsonFile("tweets.json")
tweets.registerTempTable("tweets")
val results = hiveCtx.sql("SELECT user.name, text FROM tweets")
数据库
java数据库连接
Spark可以从任何支持Java数据库连接(JDBC)的关系型数据库中读取数据,包括MySQL、Postgre等系统。
要访问这些数据,需构建一个org.apache.spark.rdd.jdbcRDD。
//Scala中的jdbcRDD
def createConnection() = {
Class.forName("com.mysql.jdbc.Driver").newInstance();82 | 第 5 章
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 ? <= id AND id <= ?",
lowerBound = 1, upperBound = 3, numPartitions = 2, mapRow = extractValues)
println(data.collect().toList)
jdbcRDD接收以下参数:
1、一个用于对数据库创建连接的函数。该函数使每个节点在连接必要的配置后创建后创建自己读取数据的连接。
2、一个可以读取一定范围内数据的查询,以及查询函数中lowerBound和upperBound的值。这些惨呼可以是Spark在不同机器上查询不同范围的数据,这样不会因尝试在一个节点上读取所有的数据而遭到==性能瓶颈==。
3、一个可以将输出结果从java.sql.Resultset转为s所需格式的数据。
如果想离线查询数据,可以使用数据库导出功能,将数据导出为文本文件。
Cassandra
使用Cassandra连接器需要添加到构筑文件中。Cassandra还没有使用Spark SQL,它会返回由CassandraRow独享组成的RDD,这些对象的一部分方法与Row对象方法相同。
//Cassandra连接器的sbt依赖
"com.datastax.spark" %% "spark-cassandra-connector" % "1.0.0-rc5",
"com.datastax.spark" %% "spark-cassandra-connector-java" % "1.0.0-rc5
//Cassandra连接器的Maven依赖
<dependency> <!-- Cassandra -->
<groupId>com.datastax.spark</groupId>
<artifactId>spark-cassandra-connector</artifactId>
<version>1.0.0-rc5</version>
</dependency>
<dependency> <!-- Cassandra -->
<groupId>com.datastax.spark</groupId>
<artifactId>spark-cassandra-connector-java</artifactId>
<version>1.0.0-rc5</version>
</dependency>
Cassandra连接器要读取一个作业属性来决定连接到哪个集群。需将spark.cassandra.connection.host设置为指向Cassandra集群。如有用户名和密码,需设置spark.cassandra.auth.username和spark.cassandra.auth.password。
//在Scala中配置Cassandra属性
val conf = new SparkConf(true)
.set("spark.cassandra.connection.host", "hostname")
val sc = new SparkContext(conf)
Datastax的Cassandra连接器使用Scala中的隐式转换来为SparkContext和RDD提供一些附加函数。
//在Scala中将整张键值对表读取为RDD
// 为SparkContext和RDD提供附加函数的隐式转换
import com.datastax.spark.connector._
// 将整张表读为一个RDD。假设你的表test的创建语句为
// CREATE TABLE test.kv(key text PRIMARY KEY, value int);
val data = sc.cassandraTable("test" , "kv")
// 打印出value字段的一些基本统计。
data.map(row => row.getInt("value")).stats()
Cassandra连接器支持把多种类型的RDD保存到Cassandra中。我们可以直接保存由CassandraRow对象组成的RDD。
//在Scala中保存数据到Cassandra
val rdd = sc.parallelize(List(Seq("moremagic", 1)))
rdd.saveToCassandra("test" , "kv", SomeColumns("key", "value"))
HBase
Spark可以功能通过Hadoop输入格式访问HBase,这个输入格式会返回键值对数据,其中键的类型为org.apache.hadoop.hbase.io.ImmutableByTesWritable。值的类型为org.apache.hadoop.hbase.client.Result。
//从HBase读取数据的Scala示例
import org.apache.hadoop.hbase.HBaseConfiguration
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])