1. 程式人生 > 其它 >Spark Sql之pathGlobFilter 和recursiveFileLookup 選項關於分割槽的一點猜想和驗證

Spark Sql之pathGlobFilter 和recursiveFileLookup 選項關於分割槽的一點猜想和驗證

起因:

學習Spark Sql時,在官方文件看到兩個有意思的選項pathGlobFilterrecursiveFileLookup

簡單地說,兩個都是隻對基礎檔案格式生效,eg: parquet,orc,avro,json.csv,text;

pathGlobFilter是根據正則篩選要讀取的檔案;而recursiveFileLookup設定為true,就會遞迴的讀取檔案,

即讀取指定目錄下及其子目錄下的檔案。

但是文件中各有一句描述這兩個選項

pathGlobFilter

It does not change the behavior of partition discovery.

我翻譯過來就是:它不會改變分割槽發現的行為。

recursiveFileLookup

recursiveFileLookupis used to recursively load files and it disables partition inferring. If data source explicitly specifies thepartitionSpecwhenrecursiveFileLookupis 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。