VectorizedReader和ORC
spark SQL not only SQL
1.SparkSession/DataFrame/Datasets API
2.Catalyst Optimization & Tungsten Execution
3.DataSource Connectors/ Spark Core(RDD API)
優化儘可能的發生晚些,因為spark SQL,可以通過函式和庫優化
整體的優化使用庫和sql/dataframe
RUN EXPLAIN plan
Interpret plan
tune plan
optimizer:
使用啟發式和代價重寫查詢計劃
column pruning:列裁剪, outer join elimination:消除outer join
Predicate push down:謂詞下推, constraint propagation:約束傳播(broadcast)
constant floding:常量累加: join reordering: join重排序
.....
spark.sql.autoBroadcastJoin.threshold
keep the statistics updated
broadcastJoin Hint
memory manager:
跟蹤記憶體的使用,有效的分配記憶體在task和運算元
code generator: 編譯物理計劃到優化後的java程式碼
Tungsten Engine: 高效的二進位制資料格式和資料結構對cpu和記憶體的高效。
調整spark.sql.codegen.hugeMethodLimit去避免較大的方法(>8k), 因為這不能被JIT編譯器
所編譯。
spark分為計算和儲存:
完整的資料流:
外部儲存給spark 喂資料
spark處理資料
如果spark處理資料很快,資料來源就可能稱為瓶頸。
更高效的去讀取柱狀的向量化資料
更高效的使用jvm生成simd 說明
指定的檔案系統可以完成跳過不必要的資料和預shuffle,可以通過不必要的shuffle和IO來加速查詢
選擇支援向量化讀取的資料來源(parquet,orc)
基於檔案的資料來源,儘可能的建立分割槽,桶。
Spark 2.3.0支援ORC Vectorized向量化原始碼分析
在Spark2.3.0的release文件中,提到ORC Vectored帶來的效能提升:
提高scan吞吐2-5倍;
開啟條件:spark.sql.orc.impl=native;
ORC 檔案型別
當然該ISSUE的提出還是有些背景的(https://issues.apache.org/jira/browse/SPARK-16060),ORC檔案格式本身是Hortonworks提出的針對Hive查詢的一種列式儲存方案,ORC是在一定程度上擴充套件了RCFile,是對RCFile的優化。有別於Facebook的RCFile型別,ORC有如下優點:
- ORCFile在RCFile基礎上引申出來Stripe和Footer等。每個ORC檔案首先會被橫向切分成多個Stripe,而每個Stripe內部以列儲存,所有的列儲存在一個檔案中,而且每個stripe預設的大小是250MB,相對於RCFile預設的行組大小是4MB,所以比RCFile更高效;
- ORCFile擴充套件了RCFile的壓縮,除了Run-length(遊程編碼),引入了字典編碼和Bit編碼;
- ORCFile儲存了檔案更多的元資訊;
其儲存格式如下:
IndexData中儲存了該stripe上資料的位置資訊,總行數等資訊
RowData以stream的形式儲存了資料的具體資訊
Stripe Footer中包含該stripe的統計結果,包括Max,Min,count等資訊
IndexData
RowData
StripeFooter
...
FileFooter中包含該表的統計結果,以及各個Stripe的位置資訊
Postscripts中儲存該表的行數,壓縮引數,壓縮大小,列等資訊
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
ORC Vectored使用場景
spark.sql.orc.impl=native的判斷
其中spark.sql.orc.impl
有native和hive兩種選擇,如果針對orc型別選擇hive格式直接呼叫org.apache.spark.sql.hive.orc.OrcFileFormat
類實現類的載入,而如果為native則會基於org.apache.spark.sql.execution.datasources.orc.OrcFileFormat
類進行載入。
/** Given a provider name, look up the data source class definition. */
def lookupDataSource(provider: String, conf: SQLConf): Class[_] = {
val provider1 = backwardCompatibilityMap.getOrElse(provider, provider) match {
case name if name.equalsIgnoreCase("orc") &&
conf.getConf(SQLConf.ORC_IMPLEMENTATION) == "native" =>
classOf[OrcFileFormat].getCanonicalName
case name if name.equalsIgnoreCase("orc") &&
conf.getConf(SQLConf.ORC_IMPLEMENTATION) == "hive" =>
"org.apache.spark.sql.hive.orc.OrcFileFormat"
case name => name
}
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
org.apache.spark.sql.execution.datasources.orc.OrcFileFormat
什麼時候支援ORC Vectored?
override def supportBatch(sparkSession: SparkSession, schema: StructType): Boolean = {
val conf = sparkSession.sessionState.conf
conf.orcVectorizedReaderEnabled && conf.wholeStageEnabled &&
schema.length <= conf.wholeStageMaxNumFields &&
schema.forall(_.dataType.isInstanceOf[AtomicType])
}
- 1
- 2
- 3
- 4
- 5
- 6
需要滿足以下條件:
* 開啟spark.sql.orc.enableVectorizedReader
: 預設true;
* 開啟spark.sql.codegen.wholeStage
: 預設true並且其scheme的長度不大於wholeStageMaxNumFields(預設100列);
* [關鍵]所有列資料型別需要為AtomicType型別的;
AtomicType型別,可根據定義檢視:
/**
* An internal type used to represent everything that is not null, UDTs, arrays, structs, and maps.
*/
protected[sql] abstract class AtomicType extends DataType {
private[sql] type InternalType
private[sql] val tag: TypeTag[InternalType]
private[sql] val ordering: Ordering[InternalType]
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
AtomicType代表了非 null/UDTs/arrays/structs/maps型別。所以如果所含列中如果包含null/UDTs/arrays/structs/maps型別,依然無法收到該ISSUE的便利。
ORC Vectored實現
OrcColumnarBatchReader的使用
在OrcFileFormat.buildReaderWithPartitionValues中:
if (enableVectorizedReader) {
val batchReader = new OrcColumnarBatchReader(
enableOffHeapColumnVector && taskContext.isDefined, copyToSpark)
// SPARK-23399 Register a task completion listener first to call `close()` in all cases.
// There is a possibility that `initialize` and `initBatch` hit some errors (like OOM)
// after opening a file.
val iter = new RecordReaderIterator(batchReader)
Option(TaskContext.get()).foreach(_.addTaskCompletionListener(_ => iter.close()))
// 呼叫initialize函式
batchReader.initialize(fileSplit, taskAttemptContext)
// 呼叫initBatch
batchReader.initBatch(
reader.getSchema,
requestedColIds,
requiredSchema.fields,
partitionSchema,
file.partitionValues)
// 生成iter
iter.asInstanceOf[Iterator[InternalRow]]
} else {
val orcRecordReader = new OrcInputFormat[OrcStruct]
.createRecordReader(fileSplit, taskAttemptContext)
val iter = new RecordReaderIterator[OrcStruct](orcRecordReader)
Option(TaskContext.get()).foreach(_.addTaskCompletionListener(_ => iter.close()))
val fullSchema = requiredSchema.toAttributes ++ partitionSchema.toAttributes
val unsafeProjection = GenerateUnsafeProjection.generate(fullSchema, fullSchema)
val deserializer = new OrcDeserializer(dataSchema, requiredSchema, requestedColIds)
if (partitionSchema.length == 0) {
iter.map(value => unsafeProjection(deserializer.deserialize(value)))
} else {
val joinedRow = new JoinedRow()
iter.map(value =>
unsafeProjection(joinedRow(deserializer.deserialize(value), file.partitionValues)))
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
OrcColumnarBatchReader的實現
initialize(): 初始化OrcFile Reader及Hadoop環境配置;
initBatch(): 初始化batch變數和columnarBatch變數(其中batch為ORC Reader向量化每次讀取的結果儲存變數,columnarBatch為codegen轉換為Spark定義型別儲存變數 );
nextBatch(): 迭代器,其核心還是呼叫ORC自定義的vectored函式,需要根據型別轉換Spark定義type;
單元測試
參考: org.apache.spark.sql.hive.orc.OrcReadBenchmark
結果分析:
針對數字、String型別測試
Native ORC Vectorized > Native ORC Vectorized with copy > Native ORC MR > Hive built-in ORC
針對分割槽、不分割槽測試
Partition效能遠遠>不分割槽效能
參考:
相關文章
- hive orc表'orc.create.index'='true'與'orc.create.index'='false'HiveIndexFalse
- Hadoop Streaming 讀ORC檔案Hadoop
- 大資料檔案格式比較:AVRO vs. PARQUET vs. ORC大資料VR
- 路徑中./和../和/
- ../和./和/的區別
- not in 和 not exists 比較和用法
- xftp和xshell,xftp和xshell的下載和安裝FTP
- #和&
- !=和<>
- 字首和與二維字首和
- @NotEmpty和@NotBlank和@NotNull小結Null
- ABAP和Java的destination和JNDIJava
- ♻️同步和非同步;並行和併發;阻塞和非阻塞非同步並行
- XML基本操作-建立(DOM和LOINQ)和LINQ查詢和儲存XML
- ThymeleafViewResolver和SpringTemplateEngine和SpringResourceTemplateResolver的關係ViewSpring
- javafx 和swing_整合JavaFX和SwingJava
- 檔案路徑問題( ./ 和 ../ 和 @/ )
- 尤拉計劃739:和的和
- workman 和swoole 區別 和異同
- 寬鬆相等和嚴格相等(==和===)
- 淺談mouseenter和mouseover,mouseout和mouseleave
- csv和excel讀取和下載Excel
- Cookie 和 Session 關係和區別CookieSession
- 堆和棧的概念和區別
- 【-Flutter/Dart 語法補遺-】 sync* 和 async* 、yield 和yield* 、async 和 awaitFlutterDartAI
- 字首和
- equals 和 ==
- if if和if else if
- ul和
- RestController和Controller的區別和異同RESTController
- Instruction和Question的區別和聯絡Struct
- (Day4)字首和&二維字首和
- vue和react的相同點和不同點VueReact
- pyinstaller和wordcloud和jieba的使用案列CloudJieba
- DOORS和Reqtify—需求管理和需求追溯工具QT
- MySQL 裡的 find_in_set () 和 in () 和 likeMySql
- DOORS和Reqtify — 需求管理和需求追溯工具QT
- count (*) 和 count (1) 和 count (列名) 區別