N8n 隨著時間變慢(每月超過 40k+ 次執行)

描述問題/錯誤/問題

我們在 Railway 上運行自託管的 n8n 實例,隨著使用量的增長,我們一直在經歷性能問題。
我們目前每月處理超過 40,000 個工作流執行。
主要問題是應該在大約 1 秒內完成的工作流現在經常需要 3-4 秒才能完成,儘管工作流邏輯本身並未改變。
我們還看到執行隊列隨機形成。根據我們目前的配置,這不應該發生,因為我們還沒有故意配置任何並發限制。
最近,我們還開始看到這個錯誤:
This execution failed to be processed too many times and will no longer retry. To allow this execution to complete, please break down your workflow or scale up your workers or adjust your worker settings.

老實說,我們不知道還能嘗試什麼。我們的 worker、主實例和 PostgreSQL 的 Railway 指標看起來都很健康,沒有明顯的資源瓶頸。有人經歷過類似的情況或對我們接下來應該調查什麼有任何想法嗎?

錯誤訊息是什麼(如果有的話)?

請分享您的工作流

(在畫布上選擇節點,使用鍵盤快捷鍵 CMD+C/CTRL+C 和 CMD+V/CTRL+V 複製和貼上工作流。)

分享最後一個節點返回的輸出

有關您的 n8n 設定的信息

  • n8n 版本:2.28.4
  • 資料庫:PostgreSQL
  • n8n EXECUTIONS_PROCESS 設定(預設值:own, main):
  • 透過以下方式運行 n8n(Docker、npm、n8n cloud、桌面應用):railway
  • 作業系統:windows 11

@jptg

根據你看到的症狀和錯誤訊息,你的 n8n 實例很可能受到流程開銷資料庫膨脹的困擾,這在自託管實例擴展到每月 40,000 次以上的執行時很常見。

錯誤訊息「This execution failed to be processed too many times」通常發生在以下情況:執行被工作程式拾起,但工作程式在鎖定過期前未能「檢入」或完成。系統會假設工作程式已當機,並重試該工作直到達到限制。

你提到了 EXECUTIONS_PROCESS 設定。如果你使用預設的 own 模式,n8n 會為每一個執行產生全新的 Node.js 流程。

  • **問題所在:**這會增加顯著的開銷(CPU 和 RAM),並為每個工作流程建立 1–3 秒的「冷啟動」延遲。隨著你的使用量增加,這會給作業系統排程器和記憶體帶來巨大壓力。
  • **解決方案:**將你的環境變數改為:EXECUTIONS_PROCESS=main

如果你以佇列模式執行 n8n(使用單獨的工作程式),你很可能遇到鎖定逾時。即使工作流程只需 4 秒,資料庫爭用或 Railway 上的網路抖動也可能導致「心跳」失敗。

  • **解決方案:**增加鎖定期間以給工作程式更多喘息空間。新增此環境變數:QUEUE_WORKER_LOCK_DURATION=120000(這會將鎖定從 60 秒增加到 120 秒)。

Railway 指標顯示 CPU/RAM,但它們不會顯示 PostgreSQL 表膨脹。在每月 40,000 次以上的執行情況下,你的 execution_entity 表可能會變得巨大,減慢 n8n 用來管理佇列的查詢速度。

  • **調查方向:**檢查你是否啟用了執行修剪。如果資料庫膨脹,即使「健康」的 CPU 指標也無法拯救你免於緩慢的 I/O。
  • **解決方案:**確保設定這些變數以保持資料庫精簡:
    • EXECUTIONS_DATA_PRUNE=true
    • EXECUTIONS_DATA_MAX_AGE=168(修剪 7 天以前的資料;可視需要調整)。
    • EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000(限制總記錄數)。

由於你沒有設定並行限制,突然的 webhook 爆發可能會觸發數十個同時流程,導致系統出現「隨機佇列」和效能下降。

  • **解決方案:**設定全域上限以保護你的實例:N8N_CONCURRENCY_PRODUCTION_LIMIT=10(從 10 開始,如果你的 Railway 資源允許,可以增加)。

嗨,感謝你的詳細回覆!

我們已經在運行 Queue Mode,現在已經應用了幾乎所有你的建議。

  • 我們已經配置了 Queue Mode。
  • 我們已啟用執行修剪(EXECUTIONS_DATA_PRUNEEXECUTIONS_DATA_MAX_AGE 等)。
  • 我們也增加了 worker 鎖定持續時間。

我們沒有應用的唯一建議是 EXECUTIONS_PROCESS=main,因為根據我們的理解,這個設定在最近的 n8n 版本中已被棄用,且在使用 Queue Mode 時不適用。

目前我們還沒有機會驗證這些更改是否改善了情況,因為效能下降是間歇性發生的。我們正在等待下一次事件出現,看看問題是否會再次出現。

對我來說,我會執行多個 n8n 實例來完全避免這個問題。

我上一個職位中,我為公司的每個部門都有一個實例

服務
營運
行銷
人力資源
開發(我的工作流程墓地)

這也促使我為它們創建了一個管理平台 lol

你實際上做了對的事情——佇列模式、修剪、鎖定碰撞——你說得沒錯,EXECUTIONS_PROCESS 已被淘汰(自有進程模式已移除;在現代 n8n 上一切都是主進程或佇列/工作進程,所以對你來說那個是無操作)。問題是你一次改變了四個變數卻沒有基準,所以即使有改善你也無法判斷是哪個旋鈕做到的。在調整更多之前,先量測——以下是如何判斷你實際有三個常見瓶頸中的哪一個的方法:資料庫 I/O、工作進程爭用,或單一洩漏的工作流程。

啟用修剪不會回收已經存在的東西。 EXECUTIONS_DATA_PRUNE 只會阻止新行從現在開始超過你的閾值——它不會縮小已經膨脹的表。在 Postgres 中這些被刪除的行變成死元組,而 execution_data(有效負載表——大的那個,不是 execution_entity)加上它的索引的磁碟大小保持很大,直到它被真空清理,而自動真空清理通常跟不上已經變得很大的表。所以「我們啟用了修剪但什麼都沒改變」正是你在慢速是資料庫 I/O 時期望的情況。直接檢查:

  • SELECT relname, n_live_tup, n_dead_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC; — 如果 execution_data 有數百萬死元組且 last_autovacuum 很久以前或為空,那就是你的答案。
  • SELECT pg_size_pretty(pg_total_relation_size('execution_data')); — 如果物理大小很大,未來的修剪幫不了你;你需要執行 pg_repack(線上執行,無長鎖定)來實際回收它。VACUUM FULL 也可以但會鎖定表。
  • 你的錯誤訊息是特定信號,不是一般性的慢速。 「這個執行失敗處理太多次」是停滯工作路徑:工作進程租用了執行,在鎖定過期前沒有完成或心跳,它被重新佇列,在 N 次嘗試後被標記為失敗。你的鎖定碰撞只在原因確實是長時間執行時才有幫助。在 Railway 上咬人且躲在「健康 CPU」後面的是工作進程達到其記憶體限制——Railway 無聲地重啟超過其 RAM 上限的容器,那個工作進程上的每個進行中執行拋出完全這個錯誤。CPU% 看起來很好是因為殺害是記憶體,不是 CPU。查看工作進程服務的重啟計數和記憶體圖形(不是 CPU)並檢查重啟是否與失敗一致。

還有一個,如果任何工作流程移動檔案。 在每月 40k 處,如果你處理 PDF/影像且二進位資料模式仍是預設,那是記憶體 + 資料庫膨脹源,它在多個工作進程間也不可靠(檔案落在一個工作進程的本地磁碟上)。N8N_DEFAULT_BINARY_DATA_MODE=s3 如果是的話——如果你全是 JSON 則忽略。

我會走的順序:兩個 Postgres 查詢優先(約一分鐘內判斷資料庫或排除),然後工作進程重啟/記憶體圖形(判斷 OOM),然後改變一件事並監看一個數字,這樣下一輪實際上是可測量的。

如果有用的話:從你真實的 pg_stat 輸出和工作進程指標中確定這些中哪個實際上是你的瓶頸是我作為固定 $49 書面診斷所做的那種拆解——你發送清理後的查詢結果 + 工作進程記憶體圖形,我發回根本原因和優先級修復清單,非同步,沒有通話。如果你之後想讓它被連續監看——對工作進程 OOM 重啟和佇列等待發出警報,在它們變成失敗執行前——那是 $149/月的監控設置。但先執行那兩個 Postgres 查詢;如果只是積累的膨脹,一個 pg_repack 修復它,你不會需要我。

如果他想要AI回覆,他早就用claude、chatgpt等了。

有一個後續要點,我第一次應該就納入的,因為這是讓修剪建議看起來沒有生效的原因。

如果你確實執行了兩個 pg_stat 查詢,execution_data 仍然很龐大,在得出修剪損壞的結論前,請檢查這些列是否真的被刪除,或只是標記為已刪除。n8n 的修剪分階段執行刪除,所以存在一個時間窗口,執行紀錄被標記要移除,但裝載資料列仍然物理存在,在一個繁忙且一直在重啟的實例上,被標記的待處理積壓可能會無限期地堆積。比較 execution_entity 中的列計數與 UI 顯示給你的現有執行紀錄,通常足以判斷你身處的情況,這完全改變修復方式:如果列已經消失了,你有死元組膨脹問題,需要重新打包;如果它們仍然存在,在它們實際被移除之前,任何清理都無濟於事。

後半部分在 Railway 上特別重要。pg_repack 在交換前在原始表旁邊重建表,所以需要大約等於表及其索引大小的可用磁碟空間。如果 execution_data 是大部分的容量,重新打包會執行一段時間,然後在磁碟上失敗,你會處於比開始時更糟的位置。首先檢查可用空間與 pg_total_relation_size。如果沒有足夠的迴旋餘地,更便宜的途徑是先大幅降低列計數,分批進行以免持有一個巨大的交易,然後才回收空間。

值得坦白說,如果這兩個查詢指向的是 worker 而不是資料庫,以上都不是你的問題,我會忽略它。