Spark Sql之pathGlobFilter 和recursiveFileLookup 選項關於分割槽的一點猜想和驗證
起因:
學習Spark Sql時,在官方文件看到兩個有意思的選項pathGlobFilter
和recursiveFileLookup
。
簡單地說,兩個都是隻對基礎檔案格式生效,eg: parquet,orc,avro,json.csv,text;
pathGlobFilter
是根據正則篩選要讀取的檔案;而recursiveFileLookup
設定為true,就會遞迴的讀取檔案,
即讀取指定目錄下及其子目錄下的檔案。
但是文件中各有一句描述這兩個選項
pathGlobFilter
It does not change the behavior of partition discovery.
我翻譯過來就是:它不會改變分割槽發現的行為。
recursiveFileLookup
recursiveFileLookup
is used to recursively load files and it disables partition inferring. If data source explicitly specifies thepartitionSpec
whenrecursiveFileLookup
is true, exception will be thrown.
而這句就是:recursiveFileLookup
被用於遞迴載入檔案並禁止分割槽推斷。如果recursiveFileLookup
設定為true並且資料來源明確指定了分割槽欄位,那麼將會丟擲異常。
猜想:
理解起來很奇怪,一是直譯過來很拗口,二是兩個引數都是用於從檔案中獲取資料,關分割槽有什麼關係吶?
後來一想,從檔案中讀取資料後被封裝到DataFrame中,DataFrame中可以使用saveAsTable儲存到表中,並且可以通過partitionBy方法指定分割槽欄位,是否和這個有關係吶?
驗證:
首先在resource下準備一個目錄,目錄結構如下:
name/
├── name=1234/
└── name.json
name.json內容:
{"_c0":"張三1","_c1":"24"}
{"_c0":"張三2","_c1":"25"}
{"_c0":"張三3","_c1":"26"}
{"_c0":"張三4","_c1":"27"}
{"_c0":"張三5","_c1":"28"}
程式碼如下:
val spark = SparkSessionUtils.getLocalSparkSession()
val recursiveFileLookupDF = spark.read
.option("recursiveFileLookup", "true")
.json(this.getClass.getResource("/name").getPath)
val pathGlobFilterDF = spark.read
.option("pathGlobFilter", "*.json")
.format("json")
.load(this.getClass.getResource("/name").getPath)
// 1
pathGlobFilterDF.write
.saveAsTable("pathGlobFilter1")
// 2
pathGlobFilterDF.write
.partitionBy("name")
.saveAsTable("pathGlobFilter2")
// 3
recursiveFileLookupDF.write
.saveAsTable("recursiveFileLookup1")
// 4
recursiveFileLookupDF.write
.partitionBy("_c0")
.saveAsTable("recursiveFileLookup2")
spark.sql("select * from pathGlobFilter1").show()
spark.sql("select * from pathGlobFilter2").show()
spark.sql("select * from recursiveFileLookup1").show()
spark.sql("select * from recursiveFileLookup2").show()
SparkSessionUtils類的程式碼如下:
object SparkSessionUtils {
def getLocalSparkSession(): SparkSession = {
SparkSession
.builder()
.appName("Spark SQL basic example")
.config("spark.testing.memory", "471859200")
.master("local[*]")
.getOrCreate()
}
}
輸出結果:
// 1
+-----+---+----+
| _c0|_c1|name|
+-----+---+----+
|張三1| 24|1234|
|張三2| 25|1234|
|張三3| 26|1234|
|張三4| 27|1234|
|張三5| 28|1234|
+-----+---+----+// 2
+-----+---+----+
| _c0|_c1|name|
+-----+---+----+
|張三1| 24|1234|
|張三2| 25|1234|
|張三3| 26|1234|
|張三4| 27|1234|
|張三5| 28|1234|
+-----+---+----+// 3
+-----+---+
| _c0|_c1|
+-----+---+
|張三1| 24|
|張三2| 25|
|張三3| 26|
|張三4| 27|
|張三5| 28|
+-----+---+// 4
+---+-----+
|_c1| _c0|
+---+-----+
| 26|張三3|
| 27|張三4|
| 25|張三2|
| 28|張三5|
| 24|張三1|
+---+-----+
顯著體現就是在pathGlobFilter會把目錄name=1234解析成一個欄位,名稱為name,值為1234;而recursiveFileLookup只會把它當成一個普通的目錄,遞迴載入其下的檔案。如果瞭解分割槽表,那麼就會知道分割槽表在路徑上的體現就是分割槽欄位=xxx。
然後在看一下表的目錄結構:
總結起來就是在load資料時,對於目錄類似a=b的形式,pathGlobFilter會將其a解析成一個欄位,欄位值為b;而recursiveFileLookup會忽略這個特點,只會把它當成一個普通的目錄,正符合官方文件描述的:it disables partition inferring。