Spark(16) -- 資料讀取與儲存的主要方式
1. 文字檔案輸入輸出
當我們將一個文字檔案讀取為 RDD 時,輸入的每一行都會成為RDD的一個元素。也可以將多個完整的文字檔案一次性讀取為一個pair RDD, 其中鍵是檔名,值是檔案內容。
val input = sc.textFile("./README.md")
如果傳遞目錄,則將目錄下的所有檔案讀取作為RDD。
檔案路徑支援萬用字元。
通過wholeTextFiles()對於大量的小檔案讀取效率比較高,大檔案效果沒有那麼高。
Spark通過saveAsTextFile() 進行文字檔案的輸出,該方法接收一個路徑,並將 RDD 中的內容都輸入到路徑對應的檔案中。Spark 將傳入的路徑作為目錄對待,會在那個目錄下輸出多個檔案。這樣,Spark 就可以從多個節點上並行輸出了。
result.saveAsTextFile(outputFile)
scala> sc.textFile("./README.md")
res6: org.apache.spark.rdd.RDD[String] = ./README.md MapPartitionsRDD[7] at textFile at <console>:25
scala> val readme = sc.textFile("./README.md")
readme: org.apache.spark.rdd.RDD[String] = ./README.md MapPartitionsRDD[9] at textFile at <console>:24
scala> readme.collect()
res7: Array[String] = Array(# Apache Spark, "", Spark is a fast and general cluster...
scala> readme.saveAsTextFile("hdfs://node01:8020/test")
2. JSON檔案輸入輸出
如果JSON檔案中每一行就是一個JSON記錄,那麼可以通過將JSON檔案當做文字檔案來讀取,然後利用相關的JSON庫對每一條資料進行JSON解析。
scala> import org.json4s._
import org.json4s._
scala> import org.json4s.jackson.JsonMethods._
import org.json4s.jackson.JsonMethods._
scala> import org.json4s.jackson.Serialization
import org.json4s.jackson.Serialization
scala> var result = sc.textFile("examples/src/main/resources/people.json")
result: org.apache.spark.rdd.RDD[String] = examples/src/main/resources/people.json MapPartitionsRDD[7] at textFile at <console>:47
scala> implicit val formats = Serialization.formats(ShortTypeHints(List()))
formats: org.json4s.Formats{val dateFormat: org.json4s.DateFormat; val typeHints: org.json4s.TypeHints} = org.json4s.Serialization$$anon$1@61f2c1da
scala> result.collect()
res3: Array[String] = Array({"name":"Michael"}, {"name":"Andy", "age":30}, {"name":"Justin", "age":19})
如果JSON資料是跨行的,那麼只能讀入整個檔案,然後對每個檔案進行解析。
JSON資料的輸出主要是通過在輸出之前將由結構化資料組成的 RDD 轉為字串 RDD,然後使用 Spark 的文字檔案 API 寫出去。
說白了還是以文字檔案的形式存,只是文字的格式已經在程式中轉換為JSON。
3. CSV檔案輸入輸出
讀取 CSV/TSV 資料和讀取 JSON 資料相似,都需要先把檔案當作普通文字檔案來讀取資料,然後通過將每一行進行解析實現對CSV的讀取。
CSV/TSV資料的輸出也是需要將結構化RDD通過相關的庫轉換成字串RDD,然後使用 Spark 的文字檔案 API 寫出去。
4. SequenceFile檔案輸入輸出
SequenceFile檔案是Hadoop用來儲存二進位制形式的key-value對而設計的一種平面檔案(Flat File)。
Spark 有專門用來讀取 SequenceFile 的介面。在 SparkContext 中,可以呼叫 sequenceFile[keyClass, valueClass](path)。
scala> val data=sc.parallelize(List((2,"aa"),(3,"bb"),(4,"cc"),(5,"dd"),(6,"ee")))
data: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[16] at parallelize at <console>:24
scala> data.saveAsSequenceFile("hdfs://node01:8020/sequdata")
scala> val sdata = sc.sequenceFile[Int,String]("hdfs://node01:8020/sequdata/*")
sdata: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[19] at sequenceFile at <console>:24
scala> sdata.collect()
res14: Array[(Int, String)] = Array((2,aa), (3,bb), (4,cc), (5,dd), (6,ee))
可以直接呼叫 saveAsSequenceFile(path) 儲存你的PairRDD,它會幫你寫出資料。需要鍵和值能夠自動轉為Writable型別。
5. 物件檔案輸入輸出
物件檔案是將物件序列化後儲存的檔案,採用Java的序列化機制。可以通過objectFile[k,v](path) 函式接收一個路徑,讀取物件檔案,返回對應的 RDD,也可以通過呼叫saveAsObjectFile() 實現對物件檔案的輸出。因為是序列化所以要指定型別。
scala> val data=sc.parallelize(List((2,"aa"),(3,"bb"),(4,"cc"),(5,"dd"),(6,"ee")))
data: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[20] at parallelize at <console>:24
scala> data.saveAsObjectFile("hdfs://node01:8020/objfile")
scala> import org.apache.spark.rdd.RDD
import org.apache.spark.rdd.RDD
scala> val objrdd =sc.objectFile[(Int,String)]("hdfs://node01:8020/objfile/p*")
objrdd: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[28] at objectFile at <console>:25
scala> objrdd.collect()
res20: Array[(Int, String)] = Array((2,aa), (3,bb), (4,cc), (5,dd), (6,ee))
6. Hadoop輸入輸出格式
Spark的整個生態系統與Hadoop是完全相容的,所以對於Hadoop所支援的檔案型別或者資料庫型別,Spark也同樣支援.另外,由於Hadoop的API有新舊兩個版本,所以Spark為了能夠相容Hadoop所有的版本,也提供了兩套建立操作介面.對於外部儲存建立操作而言,hadoopRDD和newHadoopRDD是最為抽象的兩個函式介面,主要包含以下四個引數.
- 輸入格式(InputFormat): 制定資料輸入的型別,如TextInputFormat等,新舊兩個版本所引用的版本分別是org.apache.hadoop.mapred.InputFormat和org.apache.hadoop.mapreduce.InputFormat(NewInputFormat)
- 鍵型別: 指定[K,V]鍵值對中K的型別
- 值型別: 指定[K,V]鍵值對中V的型別
- 分割槽值: 指定由外部儲存生成的RDD的partition數量的最小值,如果沒有指定,系統會使用預設值defaultMinSplits。
其他建立操作的API介面都是為了方便最終的Spark程式開發者而設定的,是這兩個介面的高效實現版本.例如,對於textFile而言,只有path這個指定檔案路徑的引數,其他引數在系統內部指定了預設值
相容舊版本HadoopAPI的建立操作
檔案路徑 | 輸入格式 | 鍵型別 | 值型別 | 分割槽值 | |
---|---|---|---|---|---|
textFile(path: String, minPartitions: Int = defaultMinPartitions) | path | TextInputFormat | LongWritable | Text | minSplits |
hadoopFile[K, V, F <: InputFormat[K, V]](path: String, minPartitions: Int)(implicit km: ClassTag[K], vm: ClassTag[V], fm: ClassTag[F]): RDD[(K, V)] | path | F | K | V | minSplits |
hadoopFile[K, V, F <: [K, V]](path: String)(implicit km: ClassTag[K], vm: ClassTag[V], fm: ClassTag[F]): RDD[(K, V)] | path | F | K | V | DefaultMinSplits |
hadoopFile[K, V](path: String, inputFormatClass: Class[_ <: InputFormat[K, V]], keyClass: Class[K], valueClass: Class[V], minPartitions: Int = defaultMinPartitions): RDD[(K, V)] | path | inputFormatClass | keyClass | valueClass | defaultMinPartitions |
hadoopRDD[K, V](conf: JobConf, inputFormatClass: Class[_ <: InputFormat[K, V]], keyClass: Class[K], valueClass: Class[V], minPartitions: Int = defaultMinPartitions): RDD[(K, V)] | n/a | inputFormatClass | keyClass | valueClass | defaultMinPartitions |
sequenceFile[K, V](path: String, minPartitions: Int = defaultMinPartitions)(implicit km: ClassTag[K], vm: ClassTag[V], kcf: () ⇒ WritableConverter[K], vcf: () ⇒ WritableConverter[V]): RDD[(K, V)] | path | SequenceFileInputFormat[K,V] | K | V | defaultMinPartitions |
objectFile[T](path: String, minPartitions: Int = defaultMinPartitions)(implicit arg0: ClassTag[T]): RDD[T] | path | SequenceFileInputFormat[NullWritable,BytesWritable] | NullWritable | BytesWritable | minSplits |
相容新版本HadoopAPI的建立操作
檔案路徑 | 輸入格式 | 鍵型別 | 值型別 | 分割槽值 | |
---|---|---|---|---|---|
newAPIHadoopFile[K, V, F <: InputFormat[K, V]](path: String, fClass: Class[F], kClass: Class[K], vClass: Class[V], conf: Configuration = hadoopConfiguration): RDD[(K, V)] | path | F | K | V | n/a |
newAPIHadoopFile[K, V, F <: InputFormat[K, V]](path: String)(implicit km: ClassTag[K], vm: ClassTag[V], fm: ClassTag[F]): RDD[(K, V)] | path | F | K | V | n/a |
newAPIHadoopRDD[K, V, F <: InputFormat[K, V]](conf: Configuration = hadoopConfiguration, fClass: Class[F], kClass: Class[K], vClass: Class[V]): RDD[(K, V)] | n/a | F | K | V | n/a |
注意:
- 在Hadoop中以壓縮形式儲存的資料,不需要指定解壓方式就能夠進行讀取,因為Hadoop本身有一個解壓器會根據壓縮檔案的字尾推斷解壓演算法進行解壓.
- 如果用Spark從Hadoop中讀取某種型別的資料不知道怎麼讀取的時候,上網查詢一個使用map-reduce的時候是怎麼讀取這種這種資料的,然後再將對應的讀取方式改寫成上面的hadoopRDD和newAPIHadoopRDD兩個類就行了.
讀取示例:
scala> import org.apache.hadoop.io._
import org.apache.hadoop.io._
scala> val data = sc.parallelize(Array((30,"hadoop"), (71,"hive"), (11,"cat")))
data: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[47] at parallelize at <console>:35
scala> data.saveAsNewAPIHadoopFile("hdfs://node01:8020/output4/",classOf[LongWritable] ,classOf[Text] ,classOf[org.apache.hadoop.mapreduce.lib.output.TextOutputFormat[LongWritable, Text]])
對於RDD最後的歸宿除了返回為集合和標量,也可以將RDD儲存到外部檔案系統或者資料庫中,Spark系統與Hadoop是完全相容的,所以MapReduce所支援的讀寫檔案或者資料庫型別,Spark也同樣支援.另外,由於Hadoop的API有新舊兩個版本,所以Spark為了能夠相容Hadoop所有的版本,也提供了兩套API.
將RDD儲存到HDFS中在通常情況下需要關注或者設定五個引數,即檔案儲存的路徑,key值的class型別,Value值的class型別,RDD的輸出格式(OutputFormat,如TextOutputFormat/SequenceFileOutputFormat),以及最後一個相關的引數codec(這個參數列示壓縮儲存的壓縮形式,如DefaultCodec,Gzip,Codec等等)
相容舊版API
saveAsObjectFile(path: String): Unit |
---|
saveAsTextFile(path: String, codec: Class[_ <: CompressionCodec]): Unit |
saveAsTextFile(path: String): Unit |
saveAsHadoopFile[F <: OutputFormat[K, V]](path: String)(implicit fm: ClassTag[F]): Unit |
saveAsHadoopFile[F <: OutputFormat[K, V]](path: String, codec: Class[_ <: CompressionCodec])(implicit fm: ClassTag[F]): Unit |
saveAsHadoopFile(path: String, keyClass: Class[], valueClass: Class[], outputFormatClass: Class[_ <: OutputFormat[_, ]], codec: Class[ <: CompressionCodec]): Unit |
saveAsHadoopDataset(conf: JobConf): Unit |
這裡列出的API,前面6個都是saveAsHadoopDataset的簡易實現版本,僅僅支援將RDD儲存到HDFS中,而saveAsHadoopDataset的引數型別是JobConf,所以其不僅能夠將RDD儲存到HDFS中,也可以將RDD儲存到其他資料庫中,如Hbase,MangoDB,Cassandra等.
相容新版API
saveAsNewAPIHadoopFile(path: String, keyClass: Class[], valueClass: Class[], outputFormatClass: Class[_ <: OutputFormat[_, _]], conf: Configuration = self.context.hadoopConfiguration): Unit |
---|
saveAsNewAPIHadoopFile[F <: OutputFormat[K, V]](path: String)(implicit fm: ClassTag[F]): Unit |
saveAsNewAPIHadoopDataset(conf: Configuration): Unit |
同樣的,前2個API是saveAsNewAPIHadoopDataset的簡易實現,只能將RDD存到HDFS中,而saveAsNewAPIHadoopDataset比較靈活.新版的API沒有codec的引數,所以要壓縮儲存檔案到HDFS中每需要使用hadoopConfiguration引數,設定對應mapreduce.map.output.compress.codec引數和mapreduce.map.output.compress引數.
注意:
1.如果不知道怎麼將RDD儲存到Hadoop生態的系統中,主要上網搜尋一下對應的map-reduce是怎麼將資料儲存進去的,然後改寫成對應的saveAsHadoopDataset或saveAsNewAPIHadoopDataset就可以了.
寫入示例:
scala> val read = sc.newAPIHadoopFile[LongWritable, Text, org.apache.hadoop.mapreduce.lib.input.TextInputFormat]("hdfs://node01:8020/output4/part*", classOf[org.apache.hadoop.mapreduce.lib.input.TextInputFormat], classOf[LongWritable], classOf[Text])
read: org.apache.spark.rdd.RDD[(org.apache.hadoop.io.LongWritable, org.apache.hadoop.io.Text)] = hdfs://node01:8020/output4/part* NewHadoopRDD[48] at newAPIHadoopFile at <console>:35
scala> read.map{case (k, v) => v.toString}.collect
res44: Array[String] = Array(30 hadoop, 71 hive, 11 cat)
7. 檔案系統的輸入輸出
Spark 支援讀寫很多種檔案系統, 像本地檔案系統、Amazon S3、HDFS等。
8. 資料庫的輸入輸出
8.1 關係型資料庫連線
實際開發中常常將分析結果RDD儲存至MySQL表中,使用foreachPartition函式;此外Spark中提供JdbcRDD用於從MySQL表中讀取資料。
呼叫RDD#foreachPartition函式將每個分割槽資料儲存至MySQL表中,儲存時考慮降低RDD分割槽數目和批量插入,提升程式效能。
建資料庫和表語句
CREATE DATABASE bigdata CHARACTER SET utf8;
CREATE TABLE `t_student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
SELECT * FROM t_student
支援通過Java JDBC訪問關係型資料庫。需要通過JdbcRDD進行,示例如下:
Mysql讀取:
def main (args: Array[String] ) {
val sparkConf = new SparkConf ().setMaster ("local[2]").setAppName ("JdbcApp")
val sc = new SparkContext (sparkConf)
val rdd = new org.apache.spark.rdd.JdbcRDD (
sc,
() => {
Class.forName ("com.mysql.jdbc.Driver").newInstance()
java.sql.DriverManager.getConnection ("jdbc:mysql://localhost:3306/rdd", "root", "hive")
},
"select * from rddtable where id >= ? and id <= ?;",
1,
10,
1,
r => (r.getInt(1), r.getString(2)))
println (rdd.count () )
rdd.foreach (println (_) )
sc.stop ()
}
Mysql寫入:
def main(args: Array[String]) {
val sparkConf = new SparkConf().setMaster("local[2]").setAppName("HBaseApp")
val sc = new SparkContext(sparkConf)
val data = sc.parallelize(List("Female", "Male","Female"))
data.foreachPartition(insertData)
}
def insertData(iterator: Iterator[String]): Unit = {
Class.forName ("com.mysql.jdbc.Driver").newInstance()
//將資料存入到MySQL
//獲取連線
val conn = java.sql.DriverManager.getConnection("jdbc:mysql://localhost:3306/rdd", "root", "admin")
iterator.foreach(data => {
//將每一條資料存入到MySQL
val ps = conn.prepareStatement("insert into rddtable(name) values (?)")
ps.setString(1, data)
ps.executeUpdate()
})
}
JdbcRDD 接收這樣幾個引數。
- 首先,要提供一個用於對資料庫建立連線的函式。這個函式讓每個節點在連線必要的配置後建立自己讀取資料的連線。
- 接下來,要提供一個可以讀取一定範圍內資料的查詢,以及查詢引數中lowerBound和 upperBound 的值。這些引數可以讓 Spark 在不同機器上查詢不同範圍的資料,這樣就不會因嘗試在一個節點上讀取所有資料而遭遇效能瓶頸。
- 這個函式的最後一個引數是一個可以將輸出結果從轉為對運算元據有用的格式的函式。如果這個引數空缺,Spark會自動將每行結果轉為一個物件陣列。
Cassandra資料庫和ElasticSearch整合:
8.2 HBase資料庫
由於 org.apache.hadoop.hbase.mapreduce.TableInputFormat 類的實現,Spark 可以通過Hadoop輸入格式訪問HBase。這個輸入格式會返回鍵值對資料,其中鍵的型別為org. apache.hadoop.hbase.io.ImmutableBytesWritable,而值的型別為org.apache.hadoop.hbase.client.Result。
Spark可以從HBase表中讀寫(Read/Write)資料,底層採用TableInputFormat和TableOutputFormat方式,與MapReduce與HBase整合完全一樣,使用輸入格式InputFormat和輸出格式OutputFoamt。
8.2.1 HBase Sink
回顧MapReduce向HBase表中寫入資料,使用TableReducer,其中OutputFormat為TableOutputFormat,讀取資料Key:ImmutableBytesWritable,Value:Put。
寫入資料時,需要將RDD轉換為RDD[(ImmutableBytesWritable, Put)]型別,呼叫saveAsNewAPIHadoopFile方法資料儲存至HBase表中。
HBase Client連線時,需要設定依賴Zookeeper地址相關資訊及表的名稱,通過Configuration設定屬性值進行傳遞。
範例演示:將詞頻統計結果儲存HBase表,表的設計
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.Put
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 將RDD資料儲存至HBase表中
*/
object SparkWriteHBase {
def main(args: Array[String]): Unit = {
// 建立應用程式入口SparkContext例項物件
val sc: SparkContext = {
// 1.a 建立SparkConf物件,設定應用的配置資訊
val sparkConf: SparkConf = new SparkConf()
.setAppName(this.getClass.getSimpleName.stripSuffix("$"))
.setMaster("local[2]")
// 1.b 傳遞SparkConf物件,構建Context例項
new SparkContext(sparkConf)
}
sc.setLogLevel("WARN")
// TODO: 1、構建RDD
val list = List(("hadoop", 234), ("spark", 3454), ("hive", 343434), ("ml", 8765))
val outputRDD: RDD[(String, Int)] = sc.parallelize(list, numSlices = 2)
// TODO: 2、將資料寫入到HBase表中, 使用saveAsNewAPIHadoopFile函式,要求RDD是(key, Value)
// TODO: 組裝RDD[(ImmutableBytesWritable, Put)]
/**
* HBase表的設計:
* 表的名稱:htb_wordcount
* Rowkey: word
* 列簇: info
* 欄位名稱: count
*/
val putsRDD: RDD[(ImmutableBytesWritable, Put)] = outputRDD.mapPartitions{ iter =>
iter.map { case (word, count) =>
// 建立Put例項物件
val put = new Put(Bytes.toBytes(word))
// 新增列
put.addColumn(
// 實際專案中使用HBase時,插入資料,先將所有欄位的值轉為String,再使用Bytes轉換為位元組陣列
Bytes.toBytes("info"), Bytes.toBytes("cout"), Bytes.toBytes(count.toString)
)
// 返回二元組
(new ImmutableBytesWritable(put.getRow), put)
}
}
// 構建HBase Client配置資訊
val conf: Configuration = HBaseConfiguration.create()
// 設定連線Zookeeper屬性
conf.set("hbase.zookeeper.quorum", "node1.itcast.cn")
conf.set("hbase.zookeeper.property.clientPort", "2181")
conf.set("zookeeper.znode.parent", "/hbase")
// 設定將資料儲存的HBase表的名稱
conf.set(TableOutputFormat.OUTPUT_TABLE, "htb_wordcount")
/*
def saveAsNewAPIHadoopFile(
path: String,// 儲存的路徑
keyClass: Class[_], // Key型別
valueClass: Class[_], // Value型別
outputFormatClass: Class[_ <: NewOutputFormat[_, _]], // 輸出格式OutputFormat實現
conf: Configuration = self.context.hadoopConfiguration // 配置資訊
): Unit
*/
putsRDD.saveAsNewAPIHadoopFile(
"datas/spark/htb-output-" + System.nanoTime(), //
classOf[ImmutableBytesWritable], //
classOf[Put], //
classOf[TableOutputFormat[ImmutableBytesWritable]], //
conf
)
// 應用程式執行結束,關閉資源
sc.stop()
}
}
執行完成以後,使用hbase shell檢視資料:
8.2.2 HBase Source
回顧MapReduce從讀HBase表中的資料,使用TableMapper,其中InputFormat為TableInputFormat,讀取資料Key:ImmutableBytesWritable,Value:Result。
從HBase表讀取資料時,同樣需要設定依賴Zookeeper地址資訊和表的名稱,使用Configuration設定屬性,形式如下:
此外,讀取的資料封裝到RDD中,Key和Value型別分別為:ImmutableBytesWritable和Result,不支援Java Serializable導致處理資料時報序列化異常。設定Spark Application使用Kryo序列化,效能要比Java 序列化要好,建立SparkConf物件設定相關屬性,如下所示:
範例演示:
從HBase表讀取詞頻統計結果,程式碼如下
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{CellUtil, HBaseConfiguration}
import org.apache.hadoop.hbase.client.Result
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableInputFormat
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 從HBase 表中讀取資料,封裝到RDD資料集
*/
object SparkReadHBase {
def main(args: Array[String]): Unit = {
// 建立應用程式入口SparkContext例項物件
val sc: SparkContext = {
// 1.a 建立SparkConf物件,設定應用的配置資訊
val sparkConf: SparkConf = new SparkConf()
.setAppName(this.getClass.getSimpleName.stripSuffix("$"))
.setMaster("local[2]")
// TODO: 設定使用Kryo 序列化方式
.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
// TODO: 註冊序列化的資料型別
.registerKryoClasses(Array(classOf[ImmutableBytesWritable], classOf[Result]))
// 1.b 傳遞SparkConf物件,構建Context例項
new SparkContext(sparkConf)
}
sc.setLogLevel("WARN")
// TODO: a. 讀取HBase Client 配置資訊
val conf: Configuration = HBaseConfiguration.create()
conf.set("hbase.zookeeper.quorum", "node1.itcast.cn")
conf.set("hbase.zookeeper.property.clientPort", "2181")
conf.set("zookeeper.znode.parent", "/hbase")
// TODO: b. 設定讀取的表的名稱
conf.set(TableInputFormat.INPUT_TABLE, "htb_wordcount")
/*
def newAPIHadoopRDD[K, V, F <: NewInputFormat[K, V]](
conf: Configuration = hadoopConfiguration,
fClass: Class[F],
kClass: Class[K],
vClass: Class[V]
): RDD[(K, V)]
*/
val resultRDD: RDD[(ImmutableBytesWritable, Result)] = sc.newAPIHadoopRDD(
conf, //
classOf[TableInputFormat], //
classOf[ImmutableBytesWritable], //
classOf[Result] //
)
println(s"Count = ${resultRDD.count()}")
resultRDD
.take(5)
.foreach { case (rowKey, result) =>
println(s"RowKey = ${Bytes.toString(rowKey.get())}")
// HBase表中的每條資料封裝在result物件中,解析獲取每列的值
result.rawCells().foreach { cell =>
val cf = Bytes.toString(CellUtil.cloneFamily(cell))
val column = Bytes.toString(CellUtil.cloneQualifier(cell))
val value = Bytes.toString(CellUtil.cloneValue(cell))
val version = cell.getTimestamp
println(s"\t $cf:$column = $value, version = $version")
}
}
// 應用程式執行結束,關閉資源
sc.stop()
}
}
執行結果:
HBase簡單讀取:
def main(args: Array[String]) {
val sparkConf = new SparkConf().setMaster("local[2]").setAppName("HBaseApp")
val sc = new SparkContext(sparkConf)
val conf = HBaseConfiguration.create()
//HBase中的表名
conf.set(TableInputFormat.INPUT_TABLE, "fruit")
val hBaseRDD = sc.newAPIHadoopRDD(conf, classOf[TableInputFormat],
classOf[org.apache.hadoop.hbase.io.ImmutableBytesWritable],
classOf[org.apache.hadoop.hbase.client.Result])
val count = hBaseRDD.count()
println("hBaseRDD RDD Count:"+ count)
hBaseRDD.cache()
hBaseRDD.foreach {
case (_, result) =>
val key = Bytes.toString(result.getRow)
val name = Bytes.toString(result.getValue("info".getBytes, "name".getBytes))
val color = Bytes.toString(result.getValue("info".getBytes, "color".getBytes))
println("Row key:" + key + " Name:" + name + " Color:" + color)
}
sc.stop()
}
HBase簡單寫入:
def main(args: Array[String]) {
val sparkConf = new SparkConf().setMaster("local[2]").setAppName("HBaseApp")
val sc = new SparkContext(sparkConf)
val conf = HBaseConfiguration.create()
val jobConf = new JobConf(conf)
jobConf.setOutputFormat(classOf[TableOutputFormat])
jobConf.set(TableOutputFormat.OUTPUT_TABLE, "fruit_spark")
val fruitTable = TableName.valueOf("fruit_spark")
val tableDescr = new HTableDescriptor(fruitTable)
tableDescr.addFamily(new HColumnDescriptor("info".getBytes))
val admin = new HBaseAdmin(conf)
if (admin.tableExists(fruitTable)) {
admin.disableTable(fruitTable)
admin.deleteTable(fruitTable)
}
admin.createTable(tableDescr)
def convert(triple: (Int, String, Int)) = {
val put = new Put(Bytes.toBytes(triple._1))
put.addImmutable(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes(triple._2))
put.addImmutable(Bytes.toBytes("info"), Bytes.toBytes("price"), Bytes.toBytes(triple._3))
(new ImmutableBytesWritable, put)
}
val initialRDD = sc.parallelize(List((1,"apple",11), (2,"banana",12), (3,"pear",13)))
val localData = initialRDD.map(convert)
localData.saveAsHadoopDataset(jobConf)
}
相關文章
- spark學習筆記--資料讀取與儲存Spark筆記
- Spark Streaming讀取Kafka資料兩種方式SparkKafka
- spark讀取hbase的資料Spark
- Spark讀取MongoDB資料的方法與優化SparkMongoDB優化
- Spark讀取MySQL資料SparkMySql
- java讀取倒序儲存的int型資料Java
- Mybatis讀取和儲存json型別的資料MyBatisJSON型別
- Spark讀取elasticsearch資料指南SparkElasticsearch
- java+pgsql實現儲存圖片到資料庫,以及讀取資料庫儲存的圖片JavaSQL資料庫
- iOS開發資料儲存篇—iOS中的幾種資料儲存方式iOS
- Machine Learning (3) - 介紹兩種儲存和讀取模型的方式Mac模型
- 數倉血緣關係資料的儲存與讀寫
- c++ (2-0) 從txt讀取和儲存資料C++
- C++(2) 從yml或者txt讀取和儲存資料C++
- Redis資料儲存和讀寫Redis
- 讀取和儲存Excel表Excel
- Web3證明資料的儲存方式Web
- Python中檔案讀取與儲存程式碼示例Python
- 在 SAP BTP Kyma Runtime 上使用 Redis 讀取和儲存資料Redis
- SpringBoot讀取配置資料的幾種方式Spring Boot
- Java讀取暫存器資料的方法Java
- 單細胞資料 儲存方式彙總
- Git儲存內容的位置與方式Git
- Android回顧--(十二) 資料儲存的幾種方式Android
- Python常用的資料儲存方式有哪些?五種!Python
- NOPI讀取Word模板並儲存
- spark讀取hdfs資料本地性異常Spark
- 關於spark雙引號--用spark清洗資料踩過的坑(spark和Python儲存csv的區別);以及調pg庫還是api獲取資料的策略SparkPythonAPI
- Pandas之EXCEL資料讀取/儲存/檔案分割/檔案合併Excel
- Spark SQL使用簡介(3)--載入和儲存資料SparkSQL
- 推動資料儲存方式變革的因素(附原資料表)
- 樹狀資料結構儲存方式—— CUD 篇資料結構
- Python資料儲存方式有幾種?如何使用?Python
- 重新學習Mysql資料庫3:Mysql儲存引擎與資料儲存原理MySql資料庫儲存引擎
- 資料儲存--檔案儲存
- Spark in action on Kubernetes - 儲存篇Spark
- 大資料儲存平臺之異構儲存實踐深度解讀大資料
- 多種方式讀取 MySQL 資料庫配置MySql資料庫