溢寫到磁碟

概觀

在記憶體密集型操作的情況下,Presto 允許將中間操作結果卸載到磁碟。此機制的目標是能夠執行需要超過每個查詢或每個節點限制的記憶體量的查詢。

此機制類似於作業系統層級的分頁交換。但是,它是在應用程式層級實作的,以解決 Presto 的特定需求。

與溢寫相關的屬性在 溢寫屬性 中說明。

記憶體管理與溢寫

預設情況下,如果查詢執行所要求的記憶體超過工作階段屬性 query_max_memoryquery_max_memory_per_node,Presto 會終止查詢。此機制可確保公平地將記憶體分配給查詢,並防止因記憶體分配而導致的死鎖。當叢集中存在許多小型查詢時,它很有效率,但會導致終止不符合限制的大型查詢。

為了克服此效率低下,引入了可撤銷記憶體的概念。查詢可以要求不計入限制的記憶體,但此記憶體可以隨時由記憶體管理員撤銷。當記憶體被撤銷時,查詢執行器會將中間資料從記憶體溢寫到磁碟,然後繼續處理。

實際上,當叢集處於閒置狀態且所有記憶體都可用時,記憶體密集型查詢可能會使用叢集中的所有記憶體。另一方面,當叢集沒有太多可用記憶體時,相同的查詢可能會被迫使用磁碟作為中間資料的儲存空間。與完全在記憶體中執行的查詢相比,被迫溢寫到磁碟的查詢可能會具有長數個數量級的執行時間。

為了在仍然允許較大型查詢利用溢寫到磁碟的同時實現更一致的查詢效能,可以將 experimental.query-limit-spill-enabled 設定為 true。每當查詢的記憶體使用量超過每個節點的總記憶體限制時,即使記憶體池未滿,此屬性也會觸發查詢的溢寫。如需詳細資訊,請參閱 溢寫屬性

請注意,啟用溢寫到磁碟並不能保證所有記憶體密集型查詢都能執行。有一些記憶體密集型操作尚不支援溢寫。查詢執行器也仍有可能無法將中間資料分割成足夠小的區塊,以使每個區塊都適合記憶體,進而在從磁碟載入資料時導致 記憶體不足 錯誤。

可撤銷記憶體與保留池

保留記憶體池和可撤銷記憶體都是為了應對記憶體不足的情況而設計的。當使用者記憶體池耗盡時,單一查詢將會升級到保留池。在這種情況下,僅允許該查詢繼續執行,從而降低叢集並行性。可撤銷記憶體會嘗試透過觸發溢寫來防止這種情況。保留池的大小為 query.max-total-memory-per-node。如果 query.max-total-memory-per-node 與節點上可用的總記憶體相比很大,則一般記憶體池可能沒有足夠的記憶體來執行較大型的查詢。如果啟用溢寫,則這將導致每個節點消耗大量記憶體的查詢發生過多的溢寫。如果停用溢寫,這些查詢會更快完成,因為它們會在保留池中執行。但是,這樣做也可能會顯著降低叢集並行性。在這種情況下,我們建議透過 experimental.reserved-pool-enabled 設定屬性來停用保留記憶體池。

溢寫磁碟空間

將中間結果溢寫到磁碟並將其檢索回來,就 I/O 操作而言是很昂貴的。因此,使用溢寫的查詢可能會受到磁碟限制。為了提高查詢效能,建議在單獨的本機裝置上為溢寫提供多個路徑(溢寫屬性 中的屬性 spiller-spill-path)。

不應將系統磁碟機用於溢寫,尤其是不應將其用於 JVM 正在執行和寫入記錄的磁碟機。這樣做可能會導致叢集不穩定。此外,建議監控已設定溢寫路徑的磁碟飽和度。

Presto 將溢寫路徑視為獨立磁碟(請參閱 JBOD),因此不需要將 RAID 用於溢寫。

溢寫壓縮

當啟用溢寫壓縮(溢寫屬性 中的 spill-compression-enabled 屬性)時,溢寫頁面將在它們具有足夠的可壓縮性時,使用與交換壓縮相同的實作進行壓縮。啟用此功能可以減少磁碟 I/O 量,但會以壓縮和解壓縮溢寫頁面的額外 CPU 負載為代價。

溢寫加密

當啟用溢寫加密(溢寫屬性 中的 spill-encryption-enabled 屬性)時,溢寫內容將使用隨機產生的(每個溢寫檔案)密碼金鑰進行加密。啟用此功能會降低溢寫到磁碟的效能,但可以保護溢寫資料免於從寫入磁碟的檔案中還原。

注意:某些 Java 發行版本隨附的原則檔案會限制可使用的密碼金鑰強度。溢寫加密使用 256 位元 AES 金鑰,可能需要無限制強度 JCE 原則檔案才能正常運作。

支援的操作

並非所有操作都支援溢寫到磁碟,而且每個操作處理溢寫的方式都不同。目前,此機制已針對下列操作實作。

聯結

在聯結操作期間,其中一個被聯結的表格會儲存在記憶體中。這個表格稱為建置表格(build table)。另一個表格的資料列會串流通過,如果它們與建置表格中的資料列匹配,則會傳遞到下一個操作。聯結操作中最消耗記憶體的部分就是這個建置表格。

當任務並行性大於一時,建置表格會被分割。分割的數量等於 task.concurrency 配置參數的值(請參閱任務屬性)。

當建置表格被分割時,溢寫到磁碟(spill-to-disk)機制可以減少聯結操作所需的峰值記憶體使用量。當查詢接近記憶體限制時,建置表格的部分分割區會被溢寫到磁碟,同時另一個表格中屬於相同分割區的資料列也會被溢寫。被溢寫的分割區數量會影響所需的磁碟空間大小。

之後,會將溢寫的分割區逐一讀回,以完成聯結操作。

透過這種機制,聯結運算子使用的峰值記憶體可以減少到最大建置表格分割區的大小。假設沒有資料傾斜,這將會是整個建置表格大小的 1 / task.concurrency 倍。

彙總

彙總函數會對一組值執行操作並傳回一個值。如果您要彙總的群組數量很大,可能需要大量的記憶體。當啟用溢寫到磁碟時,如果記憶體不足,中間累積的彙總結果會寫入磁碟。當記憶體可用時,它們會被載回並合併。

視窗

視窗函數會對一組資料列執行操作,並為每個資料列傳回一個值。如果您的視窗中的資料列數量很大,可能需要大量的記憶體。當啟用溢寫到磁碟時,如果記憶體不足,則中間結果會寫入磁碟,並在處理每個視窗時讀回。如果單個視窗過大,查詢仍然可能耗盡記憶體。

排序

當需要排序的資料列很多時,排序操作可能會使用大量記憶體。當啟用溢寫到磁碟時,如果記憶體不足,則排序後的資料列會寫入磁碟,然後在稍後在記憶體中合併回。