Paimon 跟 Spark 是否也能玩得來
來源:安瑞哥是碼農
上篇跟上上篇文章,我們聊了用 Flink API 讀寫(主要是寫) Paimon 表的一些情況,記錄了這個過程中我認為體驗不好的地方,同時也順便測試對比了一下,用 Flink API 在同一場景下寫 Hudi 跟 Paimon 表的效率差異(有興趣的同學可自行檢視歷史文章)。
我們都知道,Paimon 是屬於當下被大家所熟知的,號稱4大資料湖技術中(Hudi、Iceberg、Delta lake、Paimon) 唯一一個 Flink 的“近親”,跟 Flink 的配合自然會更加緊密。
但是,作為一款想被更多使用者所接納的技術,自然也就少不了對 Spark 這位江湖元老的支援,至少官方文件是這麼赫然寫著的。
至於 Paimon 跟 Spark 之間配合的默契程度如何,我們們今天就給它“跑起來”,看看實際效果如何?
還是老規矩,我們透過 Spark structured streaming 實時讀取 kafka 資料,然後以流方式寫入 Paimon 表來進行測試。
(PS:本文根據 kafka2.0 + Spark3.2 + Hadoop3.1 + Paimon0.6 展開)
0. 跑前準備
跑步運動員在開跑前,需要先選擇合身的裝備,穿上合腳的跑鞋,那麼這裡也一樣。
從官方文件的描述來看,paimon 只支援 Spark3.1 及以上版本,幸好,我當前使用的是 Spark3.2。
要不怎麼說「官方文件有時候過於官方」呢,跟 Flink API 遇到的情況一樣,目前雖然你可以下載到最新 0.7 版本的 paimon-spark 聯合 jar 包。
但是,maven 中央倉庫能提供的,卻依然只有0.6的孵化版本。
至於為什麼我要糾結這個,因為要在我的 Spark 專案中透過 pom 檔案的方式來引入對 paimon 的依賴。
而這,才是一個正規大資料開發的「正規玩法」。
1. 開始編碼
開發環境的基礎依賴解決之後,接下來就要著手編碼了,老規矩,還是參照官方文件一步步來。
瞧這程式碼部分寫的,那叫一個簡潔。
但我告訴你,如果你以為跟它一樣「照貓畫虎」就能輕鬆把程式調通,多少會顯得有點天真,以我這麼長時間來的趟坑經驗,它要是不給你設定點障礙,那幾乎是不太可能的事情。
知道你可能很著急,但你先別急,後面我會把遇到的坑一一告訴你。
一番折騰之後,我把調通之後的(資料正常寫入),用 Spark structured streaming 讀 kafka 資料寫 Paimon 表的程式碼提供如下:
package com.anryg.bigdata.streaming.paimon
import com.alibaba.fastjson.{JSON, JSONValidator}
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.streaming.OutputMode
/**
* @DESC: Spark 讀取 Kafka 資料寫 Paimon
* @Auther: Anryg
* @Date: 2023/12/28 11:01
*/
object SparkReadKafka2Paimon {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("SparkReadKafka2Paimon")/*.setMaster("local[*]")*/
conf.set("spark.sql.catalog.paimon","org.apache.paimon.spark.SparkCatalog") //必須設定,指定catalog為paimon
conf.set("spark.sql.catalog.paimon.warehouse","hdfs://192.168.211.106:8020/tmp/paimon") //必須設定
val spark = SparkSession.builder().config(conf).getOrCreate()
val rawDF = spark.readStream
.format("kafka")
.option("kafka.bootstrap.servers", "192.168.211.107:6667")
.option("subscribe", "test")
.option("failOnDataLoss",false)
.option("fetchOffset.numRetries",3)
.option("startingOffsets","latest")
.load()
import spark.implicits._
val ds = rawDF.selectExpr("CAST(value AS STRING)")
.map(row => {
val line = row.getAs[String]("value")
val rawJson = JSON.parseObject(line) //原始string是一個json,對其進行解析
val message = rawJson.getString("message") //獲取業務資料部分
val fieldArray = message.split(",")
fieldArray
}).filter(_.length == 9).map(array =>(array(0),array(1),array(2),array(3),array(4),array(5),array(6),array(7),array(8)))
.toDF("client_ip","domain","time","target_ip","rcode","query_type","authority_record","add_msg","dns_ip")
spark.sql("USE paimon") //指定catalog
/**跟Hudi不一樣,需要提前建立paimon表*/
spark.sql(
"""
|CREATE TABLE IF NOT EXISTS data_from_spark2paimon01(
|client_ip STRING,
|domain STRING,
|`time` STRING,
|target_ip STRING,
|rcode STRING,
|query_type STRING,
|authority_record STRING,
|add_msg STRING,
|dns_ip STRING
|)
|TBLPROPERTIES (
|'primary-key'='client_ip,domain,time,target_ip'
|)
""".stripMargin)
ds.writeStream
.outputMode(OutputMode.Append())
.option("checkpointLocation", "hdfs://192.168.211.106:8020/tmp/offset/test/SparkReadKafka2Paimon01")
.format("paimon")
.start("hdfs://192.168.211.106:8020/tmp/paimon/default.db/data_from_spark2paimon01")
.awaitTermination()
}
}
2. 幾個需要注意的地方
程式碼交代完,我們集中火力,來看看這個過程中,一共可能碰到哪些讓你“惱火”的事。
2.1 注意點一
如果根據官方文件,在 IDEA 編輯器裡,根據常規思維,寫下了 Spark 讀取 kafka 然後寫入 Paimon 的“常規程式碼”後,一執行,你就會收穫第一個報錯:
Exception in thread "main" org.apache.spark.sql.catalyst.analysis.NoSuchDatabaseException: Database 'paimon' not found
at org.apache.spark.sql.catalyst.catalog.SessionCatalog.requireDbExists(SessionCatalog.scala:218)
at org.apache.spark.sql.catalyst.catalog.SessionCatalog.setCurrentDatabase(SessionCatalog.scala:313)
at org.apache.spark.sql.connector.catalog.CatalogManager.setCurrentNamespace(CatalogManager.scala:104)
它告訴你沒有“paimon”這個資料庫,你以為自己需要新建這個庫嗎?
不,它其實是要你新增這個配置而已:
而這個“paimon”也不是什麼database,它其實是我們需要選擇的“catalog”,這裡的錯誤提示會讓人有誤解。
2.2 注意點二
解決完上面那個問題後,繼續除錯,迎接著你的2個報錯,是這個:
Exception in thread "main" java.lang.NullPointerException: Paimon 'warehouse' path must be set
at org.apache.paimon.utils.Preconditions.checkNotNull(Preconditions.java:65)
at org.apache.paimon.catalog.CatalogFactory.warehouse(CatalogFactory.java:55)
at org.apache.paimon.catalog.CatalogFactory.createCatalog(CatalogFactory.java:83)
at org.apache.paimon.catalog.CatalogFactory.createCatalog(CatalogFactory.java:66)
提示你需要設定“warehouse”的路徑。
之所以說它讓人惱火呢,原因在於,同一份程式碼,你會發現,這個資料寫入的破地址,它居然讓你配置2遍,咋想的?
你需要在程式碼裡配置這個(當然,也可以在外部的執行引數中指定,但我不太習慣):
這是第一次資料位置的指定,但為了程式能順利跑起來,還需要在後面資料寫入的時候再指定一次,這個我們下面聊。
其實以上這兩個坑,在官方文件是有提示的,只不過需要你在編碼的時候,要學會理解這些設定的含義。
透過我的驗證,這3項配置中,前2個是必須的,否則就會丟擲上面我說的異常,而第3項,目前從我的測試來看,不需要。
2.3 注意點三
第3個,可能會因為沒有設定 paimon catalog,而引發的異常如下:
Exception in thread "main" org.apache.spark.sql.AnalysisException: Hive support is required to CREATE Hive TABLE (AS SELECT);
'CreateTable `default`.`data_from_spark2paimon01`, org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe, Ignore
怎麼樣,是不是看得你一臉懵逼?
怎麼就它喵的提示建立 Hive 表失敗了呢?
原因很簡單,因為這個時候,如果你沒有指定 catalog,那麼這個程式碼中的“create table”建表語句,程式會預設你用的 hive catalog,所以會認為你在建立 hive 表,而當前這個建表語句又不符合 hive 建表語句的規範。
咋解決呢?在程式碼中手動指定 catalog。
其實對於這一點,官網也有提示,只不過,你需要知道在程式碼中如何指定:
前一個是指定 catalog,後一個是指定 database,因為預設就是 default,所以也可以不用寫。
2.4 注意點四
這個坑是我認為這幾個裡面,最坑爹的,為啥這麼說呢?
剛才上面不是提到那個“warehouse”路徑設定問題了嘛,前面是這麼設定的:
我在這裡設定了一個資料儲存的總目錄。
如果程式非得讓我在最後資料寫入的時候,還得指定一次路徑,根據我一個正常的人理解,我就會這麼設定:
在之前總目錄的基礎上,再加一個我要建的表名(data_from_spark2paimon01),以此來儲存這個表要寫進去的資料。
這樣思考按理說沒毛病吧?因為 Hudi 對於資料寫入路徑的設計就是這樣的。
可是呢,人家 Paimon 偏要膈應一些。
結果我一執行,你猜會怎麼著?
Exception in thread "main" java.lang.IllegalArgumentException: Schema file not found in location hdfs://192.168.211.106:8020/tmp/paimon/data_from_spark2paimon01. Please create table first.
at org.apache.paimon.table.FileStoreTableFactory.lambda$create$0(FileStoreTableFactory.java:61)
at java.util.Optional.orElseThrow(Optional.java:290)
它告訴你,當前這個目錄下找不到“schema file”。
我就奇了怪了,我指定的這個目錄,不就是讓你去寫資料的嗎,你的 schema file 不應該就寫到這個目錄裡面的嗎?
可它... 偏不!
至此,我們不妨來瞅一眼,這個當初我指定的目錄,到底發生了什麼變化:
看到沒,程式自作主張的在我指定的主目錄下,建立了一個“default.db”子目錄,然後在這個子目錄下,再建立了一個以表名(data_from_spark2paimon01)命名的子子目錄。
接著,在這個表名的下級目錄中,又建立了一個“schema”目錄。
我想,這個“schema”目錄,大抵就是剛才那個丟擲的異常,要尋找的吧。
於是,把剛才那個最後指定寫入路徑的地址,給改成了這個:
果然,程式終於能正常調通了。
3. 瞅一眼效果
提交到 yarn 叢集之後,跑了一個多小時,沒有出現什麼么蛾子,執行正常:
再看一眼資料目錄,寫了一共 2G 資料,共486個檔案(後續數量沒有再變少)。
從檔案數量上來看,這個小檔案量有點多(預設設定下)。
最後
從這次透過 Spark 寫 Paimon 的 API 來看,過程雖然經歷了一點小坎坷,但基本上透過仔細閱讀文件內容,再根據丟擲的異常提示,就能較快定位到問題,並迅速解決。
對於這次的測試體驗,總體感覺還可以。
雖然我還是覺得,官方文件的描述,還可以再詳細和友好一些,但比之前那種遇到異常之後,看著報錯資訊一臉懵逼,不知道如何是好要強一些。
最後,想強調一點的就是,Spark structed streaming 就是一個實時的流式計算框架,我都已經在生產上用它好幾年了,請有些同學別再誤會它了,拜託。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70027827/viewspace-3002071/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Flink流批一體是否能真正取代Spark引擎Spark
- mysqldump跟蹤匯出來東西是否排序了MySql排序
- 跟著教程操作,教你網頁上也能設計排版主圖!網頁
- 一行HTML也能畫畫?來一個!HTML
- Paimon Deletion VectorAI
- Paimon筆記AI筆記
- Spark跟Flink的JDBC,都不靠譜SparkJDBC
- 原來ReadWriteLock也能開發高效能快取,看完我也能和麵試官好好聊聊了!快取
- 原來 Canvas 也能直接繪製圓角矩形了Canvas
- 微信不新增好友也能聊天技巧 微信怎麼跟陌生人聊天?
- 我來試試這個部落格是否能發帖
- 理解 Paimon changelog producerAI
- 未來國產品牌也能擁抱這片藍天
- Hive on Spark 和 Spark sql on Hive,你能分的清楚麼HiveSparkSQL
- Hive on Spark和Spark sql on Hive,你能分的清楚麼HiveSparkSQL
- 貓也能明白系列
- JavaScript也能寫WebAssemblyJavaScriptWeb
- 芯鮮Discovery | CPU來加速,AI學習也能快又準!AI
- 沒有地圖也能導航?DeepMind用街景來認路地圖
- Paimon lookup store 實現AI
- 資料也能進超市
- Python:列表也能拆包?Python
- 當遊戲入選教材,是否能帶來一場新的革命?遊戲
- apache flink + Paimon 快速搭建指南ApacheAI
- 好程式設計師解密Spark是否可以替代hadoop程式設計師解密SparkHadoop
- Spark優化之小檔案是否需要合併?Spark優化
- 未來十年內可透過AI能實現跟狗狗對話AI
- 小學二年級數學水平,跟著這篇部落格也能理解LLM執行原理
- Vue 的使用心得,也許你也能頓悟Vue
- 少吃肉也能減碳排?
- 只聞其聲也能「看透」你,來自「聲音畫像師」的秘密
- 人工智慧是否適合使用?將來我們能代替體力勞動嗎?人工智慧
- CSS 也能自動補全字串?CSS字串
- Amazing!!CSS 也能實現極光?CSS
- 有了它,Golang 也能 Eval 了Golang
- canvas也能實現事件系統????Canvas事件
- scrapy 也能爬取妹子圖?(5)
- 也來聊聊 HTTPS.HTTP