Flink Sort-Shuffle 實現簡介

ApacheFlink發表於2021-11-18

本文介紹 Sort-Shuffle 如何幫助 Flink 在應對大規模批資料處理任務時更加遊刃有餘。主要內容包括:

  1. 資料 Shuffle 簡介
  2. 引入 Sort-Shuffle 的意義
  3. Flink Sort-Shuffle 實現
  4. 測試結果
  5. 調優引數
  6. 未來展望

Flink 作為批流一體的大資料計算引擎,大規模批資料處理也是 Flink 資料處理能力的重要組成部分。隨著 Flink 的版本迭代,其批資料處理能力也在不斷增強,sort-shuffle 的引入,使得 Flink 在應對大規模批資料處理任務時更加遊刃有餘。

一、資料 Shuffle 簡介

資料 shuffle 是批資料處理作業的一個重要階段,在這一階段中,上游處理節點的輸出資料會被持久化到外部儲存中,之後下游的計算節點會讀取這些資料並進行處理。這些持久化的資料不僅僅是一種計算節點間的資料交換形式,還在錯誤恢復中發揮著重要作用。

目前,有兩種批資料 shuffle 模型被現有的大規模分散式計算系統採用,分別是基於 hash 的方式以及基於 sort 的方式:

  1. 基於 hash 方式的核心思路是將傳送給下游不同併發消費任務的資料寫到單獨的檔案中,這樣檔案本身就成了一個自然的區分不同資料分割槽的邊界;
  2. 基於 sort 方式的核心思路是先將所有分割槽的資料寫在一起,然後通過 sort 來區分不同資料分割槽的邊界。

我們在 Flink 1.12 版本將基於 sort 的批處理 shuffle 實現引入了 Flink 並在後續進行了持續的效能與穩定性優化;到 Flink 1.13 版本,sort-shuffle 已經實現生產可用。

二、引入 Sort-Shuffle 的意義

我們之所以要在 Flink 中引入 sort-shuffle 的實現,一個重要的原因是 Flink 原本的基於 hash 的實現對大規模批作業不可用。這個也是被現有的其他大規模分散式計算系統所證明的:

  1. 穩定性方面:對於高併發批作業,基於 hash 的實現會產生大量的檔案,並且會對這些檔案進行併發讀寫,這會消耗很多資源並對檔案系統會產生較大的壓力。檔案系統需要維護大量的檔案後設資料,會產生檔案控制程式碼以及 inode 耗盡等不穩定風險。
  2. 效能方面:對於高併發批作業,併發讀寫大量的檔案意味著大量的隨機 IO,並且每次 IO 實際讀寫的資料量可能是非常少的,這對於 IO 效能是一個巨大的挑戰,在機械硬碟上,這使得資料 shuffle 很容易成為批處理作業的效能瓶頸。

通過引入基於 sort 的批資料 shuffle 實現,併發讀寫的檔案數量可以大大降低,有利於實現更好的資料順序讀寫,從而能夠提高 Flink 大規模批處理作業的穩定性與效能。除此之外,新的 sort-shuffle 實現還可以減小記憶體緩衝區的消耗。對於基於 hash 的實現,每個資料分割槽都需要一塊讀寫緩衝區,記憶體緩衝區消耗和併發成正比。而基於 sort 的實現則可以做到記憶體緩衝區消耗和作業併發解耦(儘管更大的記憶體可能會帶來更高的效能)。

更為重要的一點是我們實現了新的儲存結構與讀寫 IO 優化,這使得 Flink 的批資料 shuffle 相比於其他的大規模分散式資料處理系統更具優勢。下面的章節會更為詳細的介紹 Flink 的 sort-shuffle 實現以及所取得的結果。

三、Flink Sort-Shuffle 實現

和其他分散式系統的批資料 sort-shuffle 實現類似,Flink 的整個 shuffle 過程分為幾個重要的階段,包括寫資料到記憶體緩衝區、對記憶體緩衝區進行排序、將排好序的資料寫出到檔案以及從檔案中讀取 shuffle 資料併傳送給下游。但是,與其他系統相比,Flink 的實現有一些根本性的不同,包括多段資料儲存格式、省掉資料合併流程以及資料讀取 IO 排程等。這些都使得 Flink 的實現有著更優秀的表現。

1. 設計目標

在 Flink sort-shuffle 的整個實現過程中,我們把下面這些點作為主要的設計目標加以考量:

1.1 減少檔案數量

正如上面所討論的,基於 hash 的實現會產生大量的檔案,而減少檔案的數量有利於提高穩定性和效能。Sort-Spill-Merge 的方式被分散式計算系統廣泛採納以達到這一目標,首先將資料寫入記憶體緩衝區,當記憶體緩衝區填滿後對資料進行排序,排序後的資料被寫出到一個檔案中,這樣總的檔案數量是:(總資料量 / 記憶體緩衝區大小),從而檔案數量被減少。當所有資料寫出完成後,將產生的檔案合併成一個檔案,從而進一步減少檔案數量並增大每個資料分割槽的大小(有利於順序讀取)。

相比於其他系統的實現,Flink 的實現有一個重要的不同,即 Flink 始終向同一個檔案中不斷追加資料,而不會寫多個檔案再進行合併,這樣的好處始終只有一個檔案,檔案數量實現了最小化。

1.2 開啟更少的檔案

同時開啟的檔案過多會消耗更多的資源,同時容易導致檔案控制程式碼不夠用的問題,導致穩定性變差。因此,開啟更少的檔案有利於提升系統的穩定性。對於資料寫出,如上所述,通過始終向同一個檔案中追加資料,每個併發任務始終只開啟一個檔案。對於資料讀取,雖然每個檔案都需要被大量下游的併發任務讀取,Flink 依然通過只開啟檔案一次,並在這些併發讀取任務間共享檔案控制程式碼實現了每個檔案只開啟一次的目標。

1.3 最大化順序讀寫

檔案的順序讀寫對檔案的 IO 效能至關重要。通過減少 shuffle 檔案數量,我們已經在一定程度上減少了隨機檔案 IO。除此之外,Flink 的批資料 sort-shuffle 還實現了更多 IO 優化來最大化檔案的順序讀寫。在資料寫階段,通過將要寫出的資料緩衝區聚合成更大的批並通過 wtitev 系統呼叫寫出從而實現了更好的順序寫。在資料讀取階段,通過引入讀取 IO 排程,總是按照檔案的偏移順序服務資料讀取請求從而最大限度的實現的檔案的順序讀。實驗表明這些優化極大的提升了批資料 shuffle 的效能。

1.4 減少讀寫 IO 放大

傳統的 sort-spill-merge 方式通過將生成的多個檔案合併成一個更大的檔案從增大讀取資料塊的大小。這種實現方案雖然帶來了好處,但也有一些不足,最終要的一點便是讀寫 IO 放大,對於計算節點間的資料 shuffle 而言,在不發生錯誤的情況下,本身只需要寫入和讀取資料一次,但是資料合併使得相同的資料被讀寫多次,從而導致 IO 總量變多,並且儲存空間的消耗也會變大。

Flink 的實現通過不斷向同一個檔案中追加資料以及獨特的儲存結構規避了檔案和並的過程,雖然單個資料塊的大小小於和並後的大小,但由於規避了檔案合併的開銷再結合 Flink 獨有的 IO 排程,最終可以實現比 sort-spill-merge 方案更高的效能。

1.5 減少記憶體緩衝區消耗

類似於其他分散式計算系統中 sort-shuffle 的實現,Flink 利用一塊固定大小的記憶體緩衝區進行資料的快取與排序。這塊記憶體緩衝區的大小是與併發無關的,從而使得上游 shuffle 資料寫所需要的記憶體緩衝區大小與併發解耦。結合另一個記憶體管理方面的優化 FLINK-16428 可以同時實現下游 shuffle 資料讀取的記憶體緩衝區消耗併發無關化,從而可以減少大規模批作業的記憶體緩衝區消耗。(注:FLINK-16428 同時適用於批作業與流作業)

2. 實現細節

2.1 記憶體資料排序

在 shuffle 資料的 sort-spill 階段,每條資料被首先序列化並寫入到排序緩衝區中,當緩衝區被填滿後,會對緩衝區中的所有二進位制資料按照資料分割槽的順序進行排序。此後,排好序的資料會按照資料分割槽的順序被寫出到檔案中。雖然,目前並沒有對資料本身進行排序,但是排序緩衝區的介面足夠的泛化,可以實現後續潛在的更為複雜的排序要求。排序緩衝區的介面定義如下:

public interface SortBuffer {
 
   */** Appends data of the specified channel to this SortBuffer. \*/*
   boolean append(ByteBuffer source, int targetChannel, Buffer.DataType dataType) throws IOException;
 
   */** Copies data in this SortBuffer to the target MemorySegment. \*/*
   BufferWithChannel copyIntoSegment(MemorySegment target);
 
   long numRecords();
 
   long numBytes();
 
   boolean hasRemaining();
 
   void finish();
 
   boolean isFinished();
 
   void release();
 
   boolean isReleased();
 }

在排序演算法上,我們選擇了複雜度較低的 bucket-sort。具體而言,每條序列化後的資料前面都會被插入一個 16 位元組的後設資料。包括 4 位元組的長度、4 位元組的資料型別以及 8 位元組的指向同一資料分割槽中下一條資料的指標。結構如下圖所示:

img

當從緩衝區中讀取資料時,只需要按照每個資料分割槽的鏈式索引結構就可以讀取到屬於這個資料分割槽的所有資料,並且這些資料保持了資料寫入時的順序。這樣按照資料分割槽的順序讀取所有的資料就可以達到按照資料分割槽排序的目標。

2.2 檔案儲存結構

如前所述,每個並行任務產生的 shuffle 資料會被寫到一個物理檔案中。每個物理檔案包含多個資料區塊(data region),每個資料區塊由資料緩衝區的一次 sort-spill 生成。在每個資料區塊中,所有屬於不同資料分割槽(data partition,由下游計算節點不同並行任務消費)的資料按照資料分割槽的序號順序進行排序聚合。下圖展示了 shuffle 資料檔案的詳細結構。其中(R1,R2,R3)是 3 個不同的資料區塊,分別對應 3 次資料的 sort-spill 寫出。每個資料塊中有 3 個不同的資料分割槽,分別將由(C1,C2,C3)3 個不同的並行消費任務進行讀取。也就是說資料 B1.1,B2.1 及 B3.1 將由 C1 處理,資料 B1.2,B2.2 及 B3.2 將由 C2 處理,而資料 B1.3,B2.3 及 B3.3 將由 C3 處理。

img

類似於其他的分散式處理系統實現,在 Flink 中,每個資料檔案還對應一個索引檔案。索引檔案用來在讀取時為每個消費者索引屬於它的資料(data partition)。索引檔案包含和資料檔案相同的 data region,在每個 data region 中有與 data partition 相同數量的索引項,每個索引項包含兩個部分,分別對應到資料檔案的偏移量以及資料的長度。作為一個優化。Flink 為每個索引檔案快取最多 4M 的索引資料。資料檔案與索引檔案的對應關係如下:

img

2.3 讀取 IO 排程

為了進一步提高檔案 IO 效能,基於上面的儲存結構,Flink 進一步引入了 IO 排程機制,類似於磁碟排程的電梯演算法,Flink 的 IO 排程總是按照 IO 請求的檔案偏移順序進行排程。更具體來說,如果資料檔案有 n 個 data region,每個 data region 有 m 個 data partition,同時有 m 個下游計算任務讀取這一資料檔案,那麼下面的虛擬碼展示了 Flink 的 IO 排程演算法的工作流程:

*// let data_regions as the data region list indexed from 0 to n - 1*
 *// let data_readers as the concurrent downstream data readers queue indexed from 0 to m - 1*
 for (data_region in data_regions) {
   data_reader = poll_reader_of_the_smallest_file_offset(data_readers);
   if (data_reader == null)
     break;
   reading_buffers = request_reading_buffers();
   if (reading_buffers.isEmpty())
     break;
   read_data(data_region, data_reader, reading_buffers);
 }   

2.4 資料廣播優化

資料廣播是指傳送相同的資料給下游計算節點的所有並行任務,一個常見的應用場景是 broadcast-join。Flink 的 sort-shuffle 實現對這一過程進行了優化,使得在包括記憶體排序緩衝區和 shuffle 檔案中,廣播資料只儲存一份,這可以大大提升資料廣播的效能。更具體來說,當寫入一條廣播資料到排序緩衝區時,這條資料只會被序列化並且拷貝一次,同樣在將資料寫出到 shuffle 檔案時,也只會寫一份資料。在索引檔案中,對於不同 data partition 的資料索引項,他們均指向資料檔案中的同一塊資料。下圖展示了資料廣播優化的所有細節:

img

2.5 資料壓縮

資料壓縮是一個簡單而有效的優化手段,測試結果顯示資料壓縮可以提高 TPC-DS 總體效能超過 30%。類似於 Flink 的基於 hash 的批處理 shuffle 實現,資料壓縮是以網路緩衝區(network buffer)為單位進行的,資料壓縮不跨 data partition,也就是說發給不同下游並行任務的資料分開壓縮,壓縮發生在資料排序後寫出前,下游消費任務在收到資料後進行解壓。下圖展示了資料壓縮的整個流程:

img

四、測試結果

1. 穩定性

新的 sort-shuffle 的實現極大的提高 Flink 執行批處理作業的穩定性。除了解決了潛在的檔案控制程式碼以及 inode 耗盡的不穩定問題外,還解決了一些 Flink 原有 hash-shuffle 存在的已知問題,如 FLINK-21201(建立過多檔案導致主執行緒阻塞),FLINK-19925(在網路 netty 執行緒中執行 IO 操作導致網路穩定性受到影響)等。

2. 效能

我們在 1000 規模的併發下執行了 TPC-DS 10T 資料規模的測試,結果表明,相比於 Flink 原本的批資料 shuffle 實現,新的資料 shuffle 實現可以實現 2-6 倍的效能提升,如果排除計算時間,只統計資料 shuffle 時間可以是先最高 10 倍的效能提升。下表展示了效能提升的詳細資料:

JobsTime Used for Sort-Shuffle (s)Time Used for Hash-Shuffle (s)Speed up Factor
q4.sql98653715.45
q11.sql3487982.29
q14b.sql88321292.51
q17.sql2697812.90
q23a.sql41811992.87
q23b.sql3768432.24
q25.sql4138732.11
q29.sql35410382.93
q31.sql2234982.23
q50.sql2155502.56
q64.sql2174422.04
q74.sql2709623.56
q75.sql1667134.30
q93.sql2045402.65

在我們的測試叢集上,每塊機械硬碟的資料讀取以及寫入頻寬可以達到 160MB/s:

Disk NameSDISDJSDK
Writing Speed (MB/s)189173186
Reading Speed (MB/s)112154158

注:我們的測試環境配置如下,由於我們有較大的記憶體,所以一些 shuffle 資料量小的作業實際資料 shuffle 僅為讀寫記憶體,因此上面的表格僅列出了一些 shuffle 資料量大,效能提升明顯的查詢:

Number of NodesMemory Size Per NodeCores Per NodeDisks Per Node
12About 400G963

五、調優引數

在 Flink 中,sort-shuffle 預設是不開啟的,想要開啟需要調小這個引數的配置:taskmanager.network.sort-shuffle.min-parallelism。這個引數的含義是如果資料分割槽的個數(一個計算任務併發需要傳送資料給幾個下游計算節點)低於這個值,則走 hash-shuffle 的實現,如果高於這個值則啟用 sort-shuffle。實際應用時,在機械硬碟上,可以配置為 1,即使用 sort-shuffle。

Flink 沒有預設開啟資料壓縮,對於批處理作業,大部分場景下是建議開啟的,除非資料壓縮率低。開啟的引數為 taskmanager.network.blocking-shuffle.compression.enabled

對於 shuffle 資料寫和資料讀,都需要佔用記憶體緩衝區。其中,資料寫緩衝區的大小由 taskmanager.network.sort-shuffle.min-buffers 控制,資料讀緩衝區由 taskmanager.memory.framework.off-heap.batch-shuffle.size 控制。資料寫緩衝區從網路記憶體中切分出來,如果要增大資料寫緩衝區可能還需要增大網路記憶體總大小,以避免出現網路記憶體不足的錯誤。資料讀緩衝區從框架的 off-heap 記憶體中切分出來,如果要增大資料讀緩衝區,可能還需要增大框架的 off-heap 記憶體,以避免出現 direct 記憶體 OOM 錯誤。一般而言更大的記憶體緩衝區可以帶來更好的效能,對於大規模批作業,幾百兆的資料寫緩衝區與讀緩衝區是足夠的。

六、未來展望

還有一些後續的優化工作,包括但不限於:

1)網路連線複用,這可以提高網路的建立的效能與穩定性,相關 Jira 包括 FLINK-22643 以及 FLINK-15455;

2)多磁碟負載均衡,這有利於解決負載不均的問題,相關 Jira 包括 FLINK-21790 以及 FLINK-21789;

3)實現遠端資料 shuffle 服務,這有利於進一步提升批資料 shuffle 的效能與穩定性;

4)允許使用者選擇磁碟型別,這可以提高易用性,使用者可以根據作業的優先順序選擇使用 HDD 或者 SSD。

英文原文連結:

https://flink.apache.org/2021...

https://flink.apache.org/2021...


12 月 4-5 日,Flink Forward Asia 2021 重磅開啟,全球 40+ 多行業一線廠商,80+ 乾貨議題,帶來專屬於開發者的技術盛宴。
https://flink-forward.org.cn/

另有首屆 Flink Forward Asia Hackathon 正式啟動,10W 獎金等你來!
https://www.aliyun.com/page-s...
img


更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群
第一時間獲取最新技術文章和社群動態,請關注公眾號~

image.png
(https://img.alicdn.com/imgext...!!6000000002473-0-tps-5653-3144.jpg)

相關文章