本頁面說明如何找出並解決 Dataflow 中的記憶體不足 (OOM) 錯誤。
找出記憶體不足錯誤
如要判斷管道是否即將耗盡記憶體,請使用下列其中一種方法。
- 在「Jobs details」(工作詳細資料) 頁面的「Logs」(記錄) 窗格中,查看「Diagnostics」(診斷) 分頁標籤。 這個分頁會顯示與記憶體問題相關的錯誤,以及錯誤發生的頻率。
- 在 Dataflow 監控介面中,使用「記憶體使用率」圖表監控工作站記憶體容量和用量。
- 在「工作詳細資料」頁面的「記錄」窗格中,選取「工作站記錄」,找出工作站記錄中的記憶體不足錯誤。
系統記錄中也可能會顯示記憶體不足錯誤。如要查看這些項目,請前往記錄檔探索工具,並使用下列查詢:
resource.type="dataflow_step" resource.labels.job_id="JOB_ID" "out of memory" OR "OutOfMemory" OR "Shutting down JVM"
將 JOB_ID 替換為工作 ID。
如果是 Java 工作,Java 記憶體監控器會定期回報垃圾收集指標。如果用於垃圾收集的 CPU 時間比例在一段時間內超過 50% 的門檻,SDK 測試架構就會失敗。您可能會看到類似以下範例的錯誤:
Shutting down JVM after 8 consecutive periods of measured GC thrashing. Memory is used/total/max = ...
即使實體記憶體仍有可用空間,也可能發生這個錯誤,通常表示管道的記憶體用量效率不彰。如要解決這個問題,請最佳化管道。
Java 記憶體監控器是透過
MemoryMonitorOptions
介面設定。
如果作業的記憶體用量偏高或發生記憶體不足錯誤,請按照本頁的建議,最佳化記憶體用量或增加可用記憶體量。
解決記憶體不足錯誤
變更 Dataflow 管道可能可以解決記憶體不足錯誤,或減少記憶體用量。可能的變更包括下列動作:
下圖顯示本頁所述的 Dataflow 疑難排解工作流程。
請嘗試下列解決方法:
- 盡可能最佳化管道,減少記憶體用量。
- 如果工作是批次工作,請依序嘗試下列步驟:
- 使用每個 vCPU 記憶體較多的機器類型。
- 將執行緒數量減少至每個工作站的 vCPU 數量以下。
- 使用自訂機器類型,為每個 vCPU 分配更多記憶體。
- 如果工作是使用 Python 的串流工作,請將執行緒數量減少至 12 以下。
- 如果工作是使用 Java 或 Go 的串流工作,請嘗試下列做法:
- 將執行緒數量減少至 Runner v2 工作少於 500 個,或減少至未使用 Runner v2 的工作少於 300 個。
- 使用記憶體較大的機器類型。
最佳化管道
多項管道作業都可能導致記憶體不足錯誤。本節提供多種方法,可減少管道的記憶體用量。如要找出耗用最多記憶體的管道階段,請使用 Cloud Profiler 監控管道效能。
您可以採用下列最佳做法,提升管道成效:
- 使用 Apache Beam 內建 I/O 連接器讀取檔案
- 使用
GroupByKey
PTransforms 時重新設計作業 - 減少從外部來源傳入的資料
- 在不同執行緒之間共用物件
- 使用記憶體效率高的元素表示法
- 縮減側邊輸入的大小
- 使用 Apache Beam 可分割的 DoFn
使用 Apache Beam 內建的 I/O 連接器讀取檔案
請勿在 DoFn
中開啟大型檔案。如要讀取檔案,請使用 Apache Beam 內建的 I/O 連接器。在 DoFn
中開啟的檔案必須符合記憶體大小。由於多個 DoFn
執行個體會同時執行,在 DoFn
中開啟大型檔案可能會導致記憶體不足錯誤。
使用 GroupByKey
PTransforms 時重新設計作業
在 Dataflow 中使用 GroupByKey
PTransform 時,系統會在單一執行緒上處理每個鍵和每個時間區間的值。由於這項資料會以串流形式從 Dataflow 後端服務傳遞至工作站,因此不需要納入工作站記憶體。不過,如果值是在記憶體中收集,處理邏輯可能會導致記憶體不足錯誤。
舉例來說,如果您有一個包含視窗資料的鍵,並將鍵值新增至記憶體內物件 (例如清單),可能會發生記憶體不足錯誤。在這種情況下,工作站可能沒有足夠的記憶體容量來保存所有物件。
如要進一步瞭解 GroupByKey
PTransforms,請參閱 Apache Beam Python GroupByKey
和 Java GroupByKey
說明文件。
下列清單提供管道設計建議,可協助您在使用 GroupByKey
PTransform 時,盡量減少記憶體用量。
- 如要減少每個鍵和每個視窗的資料量,請避免使用具有多個值的鍵,也就是熱鍵。
- 如要減少每個視窗收集的資料量,請縮小視窗大小。
- 如果您要使用視窗中的鍵值計算數字,請使用
Combine
轉換。收集值後,請勿在單一DoFn
執行個體中進行計算。 - 先篩選值或重複項目,再進行處理。詳情請參閱 Python
Filter
和 JavaFilter
轉換說明文件。
減少外部來源的輸入資料
如果您要呼叫外部 API 或資料庫來擴充資料,傳回的資料必須符合工作站記憶體大小。如果您要批次處理呼叫,建議使用 GroupIntoBatches
轉換。
如果遇到記憶體不足錯誤,請減少批次大小。如要進一步瞭解如何分批處理,請參閱 Python GroupIntoBatches
和 Java GroupIntoBatches
轉換說明文件。
在不同執行緒之間共用物件
在 DoFn
執行個體之間共用記憶體內資料物件,可提升空間和存取效率。在 DoFn
的任何方法中建立的資料物件 (包括 Setup
、StartBundle
、Process
、FinishBundle
和 Teardown
) 都會針對每個 DoFn
叫用。在 Dataflow 中,每個工作站可能有多個DoFn
執行個體。如要更有效率地使用記憶體,請將資料物件做為單例項傳遞,以便在多個 DoFn
之間共用。詳情請參閱「Cache reuse across DoFn
s」網誌文章。
使用節省記憶體的元素表示法
評估是否能使用耗用較少記憶體的 PCollection
元素表示法。在管道中使用編碼器時,請考慮編碼和解碼的 PCollection
元素表示法。稀疏矩陣通常可從這類最佳化中獲益。
縮減側邊輸入的大小
如果 DoFn
使用側邊輸入,請縮減側邊輸入的大小。如果是元素集合的側邊輸入內容,請考慮使用可疊代檢視畫面 (例如 AsIterable
或 AsMultimap
),而非同時具體化整個側邊輸入內容的檢視畫面 (例如 AsList
)。
減少執行緒數量
您可以減少執行 DoFn
執行個體的執行緒數量上限,增加每個執行緒可用的記憶體。這項變更會減少平行處理,但可為每個 DoFn
提供更多記憶體。
下表顯示 Dataflow 建立的預設執行緒數量:
工作類型 | Python SDK | Java/Go SDK |
---|---|---|
批次 | 每個 vCPU 1 個執行緒 | 每個 vCPU 1 個執行緒 |
使用 Runner v2 進行串流 | 每個 vCPU 12 個執行緒 | 每個工作站 VM 500 個執行緒 |
不使用 Runner v2 進行串流 | 每個 vCPU 12 個執行緒 | 每個工作站 VM 300 個執行緒 |
如要減少 Apache Beam SDK 執行緒數量,請設定下列管道選項:
Java
使用 --numberOfWorkerHarnessThreads
管道選項。
Python
使用 --number_of_worker_harness_threads
管道選項。
Go
使用 --number_of_worker_harness_threads
管道選項。
如果是批次工作,請將值設為小於 vCPU 數的數字。
如果是串流工作,請先將值減半 (預設值的一半)。如果這個步驟無法解決問題,請繼續將值減半,並觀察每個步驟的結果。舉例來說,使用 Python 時,請嘗試 6、3 和 1。
使用每個 vCPU 記憶體較多的機器類型
如要選取每個 vCPU 具有更多記憶體的工作站,請使用下列其中一種方法。
- 在一般用途機器系列中使用高記憶體使用率機器類型。高記憶體使用率機型的每個 vCPU 記憶體都比標準機型高。使用高記憶體機器類型時,每個工作站可用的記憶體和每個執行緒可用的記憶體都會增加,因為 vCPU 數量維持不變。因此,使用高記憶體使用率機器類型,可有效率地選取每個 vCPU 具備較多記憶體的背景工作。
- 如要更彈性地指定 vCPU 數量和記憶體容量,可以使用自訂機器類型。使用自訂機器類型時,您可以 256 MB 為單位增加記憶體。這些機器類型的價格與標準機器類型不同。
- 部分機器系列可讓您使用擴充記憶體自訂機器類型。擴充記憶體可提高每個 vCPU 的記憶體比例。費用較高。
如要設定工作站類型,請使用下列管道選項。詳情請參閱「設定管道選項」和「管道選項」。
Java
使用 --workerMachineType
管道選項。
Python
使用 --machine_type
管道選項。
Go
使用 --worker_machine_type
管道選項。
只使用一個 Apache Beam SDK 程序
對於使用 Runner v2 的 Python 串流管道和 Python 管道,您可以強制 Dataflow 為每個工作站只啟動一個 Apache Beam SDK 程序。嘗試這個選項前,請先試著使用其他方法解決問題。如要將 Dataflow 工作站 VM 設定為只啟動一個容器化的 Python 程序,請使用下列管道選項:
--experiments=no_use_multiple_sdk_containers
完成這項設定後,Python 管道會為每個工作站建立一個 Apache Beam SDK 程序。這項設定可避免為每個 Apache Beam SDK 程序多次複製共用物件和資料。不過,這會限制工作站上可用運算資源的有效使用。
將 Apache Beam SDK 程序數量減少為一,不一定會減少工作站啟動的執行緒總數。此外,如果單一 Apache Beam SDK 程序上的所有執行緒都處於忙碌狀態,可能會導致處理速度緩慢,或管道停滯。因此,您可能也必須減少執行緒數量,如本頁「減少執行緒數量」一節所述。
您也可以使用只有一個 vCPU 的機器類型,強制工作站只使用一個 Apache Beam SDK 程序。
瞭解 Dataflow 記憶體用量
如要排解記憶體不足錯誤,瞭解 Dataflow 管道如何使用記憶體會很有幫助。
Dataflow 執行管道時,處理作業會分配到多個 Compute Engine 虛擬機器 (VM),通常稱為工作站。工作站會處理 Dataflow 服務中的工作項目,並將工作項目委派給 Apache Beam SDK 程序。Apache Beam SDK 程序會建立 DoFn
的執行個體。DoFn
是 Apache Beam SDK 類別,用於定義分散式處理函式。
Dataflow 會在每個工作站啟動多個執行緒,且每個工作站的記憶體會由所有執行緒共用。執行緒是在較大型程序中執行的單一可執行工作。預設執行緒數量取決於多項因素,且批次和串流作業各有不同。
如果管道需要的記憶體超過工作站可用的預設記憶體量,可能會發生記憶體不足錯誤。
Dataflow pipeline 主要有三種使用工作站記憶體的方式:
工作站運作記憶體
Dataflow 工作站需要記憶體才能執行作業系統和系統程序。工作站記憶體用量通常不會超過 1 GB。用量通常不到 1 GB。
- worker 上的各種程序會使用記憶體,確保管道正常運作。這些程序可能都會保留少量記憶體供作業使用。
- 如果管道未使用 Streaming Engine,額外的工作站程序會使用記憶體。
SDK 程序記憶體
Apache Beam SDK 程序可能會建立物件和資料,並在程序內的執行緒之間共用,在本頁中稱為 SDK 共用物件和資料。這些 SDK 共用物件和資料的記憶體用量稱為 SDK 程序記憶體。以下列出 SDK 共用物件和資料的範例:
- 側邊輸入
- 機器學習模型
- 記憶體內單例模式物件
- 使用
apache_beam.utils.shared
模組建立的 Python 物件 - 從外部來源載入的資料,例如 Cloud Storage 或 BigQuery
未使用 Streaming Engine 的串流工作會將側邊輸入儲存在記憶體中。如果是 Java 和 Go 管道,每個工作站都會有一份旁側輸入內容。如果是 Python 管道,每個 Apache Beam SDK 程序都會有一份側邊輸入內容。
使用 Streaming Engine 的串流工作,側邊輸入大小上限為 80 MB。側邊輸入內容會儲存在工作站記憶體以外的位置。
SDK 共用物件和資料的記憶體用量會隨著 Apache Beam SDK 程序數量線性成長。在 Java 和 Go 管道中,每個工作站都會啟動一個 Apache Beam SDK 程序。在 Python 管道中,每個 vCPU 會啟動一個 Apache Beam SDK 程序。在同一個 Apache Beam SDK 程序中,SDK 共用物件和資料會在各個執行緒之間重複使用。
DoFn
記憶體用量
DoFn
是 Apache Beam SDK 類別,用於定義分散式處理函式。每個工作人員都可以執行並行的 DoFn
執行個體。每個執行緒都會執行一個 DoFn
例項。評估記憶體總用量、計算工作集大小,或應用程式繼續運作所需的記憶體量時,這項資訊可能會有幫助。舉例來說,如果個別 DoFn
最多使用 5 MB 的記憶體,且工作站有 300 個執行緒,則 DoFn
記憶體用量最高可達 1.5 GB,也就是記憶體位元組數乘以執行緒數。視工作站使用記憶體的方式而定,記憶體用量暴增可能會導致工作站記憶體不足。
很難估算 Dataflow 會建立多少個 DoFn 執行個體。數量取決於各種因素,例如 SDK、機器類型等。此外,DoFn 可能會由多個執行緒接連使用。Dataflow 服務不保證 DoFn
的叫用次數,也不保證在整個管道過程中會確切建立多少個 DoFn
執行個體。不過,下表會提供一些深入分析,說明您可預期的平行處理程度,並估算 DoFn
執行個體數量的上限。
Beam Python SDK
批次 | 不使用 Streaming Engine 進行串流 | Streaming Engine | |
---|---|---|---|
平行處理工作數量 |
每個 vCPU 1 個程序 每個程序 1 個執行緒 每個 vCPU 1 個執行緒
|
每個 vCPU 1 個程序 每個程序 12 個執行緒 每個 vCPU 12 個執行緒 |
每個 vCPU 1 個程序 每個程序 12 個執行緒 每個 vCPU 12 個執行緒
|
並行 DoFn 執行個體數量上限 (所有這些數字隨時可能變更) |
每個執行緒 1 個 DoFn
每 vCPU 1
|
每個執行緒 1 個 DoFn
每個 vCPU 12
|
每個執行緒 1 個 DoFn
每個 vCPU 12
|
Beam Java/Go SDK
批次 | 沒有 Runner V2 的串流設備和串流引擎 | 搭配 Runner v2 使用 Streaming Engine | |
---|---|---|---|
平行處理工作數量 |
每個工作者 VM 1 個程序 每個 vCPU 1 個執行緒
|
每個工作者 VM 1 個程序 每個程序 300 個執行緒 每個工作站 VM 300 個執行緒
|
每個工作者 VM 1 個程序 每個程序 500 個執行緒 每個工作站 VM 500 個執行緒
|
並行 DoFn 執行個體數量上限 (所有這些數字隨時可能變更) |
每個執行緒 1 個 DoFn
每 vCPU 1
|
每個執行緒 1 個 DoFn
每個工作站 VM 300 個
|
每個執行緒 1 個 DoFn
每個工作站 VM 500 個
|
舉例來說,如果搭配 Python SDK 使用 n1-standard-2
Dataflow 工作站,則適用下列情況:
- 批次工作:Dataflow 會為每個 vCPU 啟動一個程序 (本例為兩個)。每個程序會使用一個執行緒,而每個執行緒會建立一個
DoFn
執行個體。 - 使用 Streaming Engine 的串流工作:Dataflow 會為每個 vCPU 啟動一個程序 (共兩個)。不過,每個程序最多可產生 12 個執行緒,每個執行緒都有自己的 DoFn 執行個體。
設計複雜的管道時,請務必瞭解DoFn
生命週期。請確保 DoFn
函式可序列化,並避免直接在函式中修改元素引數。
如果您有多語言管道,且工作站上執行多個 Apache Beam SDK,工作站會盡可能使用最低程度的每個程序執行緒平行處理。
Java、Go 和 Python 的差異
Java、Go 和 Python 管理程序和記憶體的方式不同。因此,排解記憶體不足錯誤時,您應採取的做法會因管線使用的語言 (Java、Go 或 Python) 而異。
Java 和 Go pipeline
在 Java 和 Go 管道中:
- 每個工作站都會啟動一個 Apache Beam SDK 程序。
- SDK 共用物件和資料 (例如輔助輸入和快取) 會在工作站的所有執行緒之間共用。
- SDK 共用物件和資料使用的記憶體通常不會根據工作站的 vCPU 數量調整。
Python 管道
在 Python 管道中:
- 每個工作站會為每個 vCPU 啟動一個 Apache Beam SDK 程序。
- SDK 共用物件和資料 (例如輔助輸入和快取) 會在每個 Apache Beam SDK 程序中的所有執行緒之間共用。
- 工作站上的執行緒總數會根據 vCPU 數量線性擴充。 因此,SDK 共用物件和資料使用的記憶體會隨著 vCPU 數量線性成長。
- 執行工作的執行緒會分散在各個程序中。新的工作單元會指派給沒有工作項目的程序,或是目前指派工作項目最少的程序。