資料與儲存 2026 年 9 月 29 日

2026-09-29 — 失控查詢壓測:RDS 存活率 0%,ClickHouse Postgres 撐住

primary=https://clickhouse.com/blog/can-your-postgres-survive-a-bad-query

失控查詢壓測:RDS 存活率 0%,ClickHouse Postgres 撐住

ClickHouse Blog · 2026-09-28

在一場刻意製造記憶體耗盡的壓力測試中,Amazon RDS 在 23 個併發連線下只有 7% 的查詢跑完、叢集存活率掉到 0%;同一測試條件下,ClickHouse Managed Postgres 的查詢完成率是 32%,叢集存活率卻維持 100%。這項測試由 ClickHouse 工程團隊於 2026 年 9 月 28 日發布,比較了四家代管 Postgres 服務在同一支失控查詢下的行為差異。

原本的問題

Postgres 本身沒有內建的「每條查詢記憶體上限」。work_mem 只限制單一 sort 或 hash 節點,平行查詢時每個 worker 又各自吃一份,聚合、排序疊加起來很容易遠超過設定值。文章舉例:預設 work_mem 是 4MB、hash_mem_multiplier 預設 2.0,一段簡單查詢的三個 process 加總用了約 33MB,已經是 work_mem 的 8 倍以上。

把 work_mem 調到 8MB 雖然消除了溢出到磁碟的排序,代價是實際用量變成約 68MB,是原本的 8.5 倍。也就是說,查詢一旦複雜或併發連線一多,記憶體用量會快速疊加,而 Postgres 沒有機制主動擋下單一查詢把整台主機的記憶體榨乾——這道防線幾乎全靠代管服務商自己加。ClickHouse 這次測試想驗證的,就是各家防護層在真正被壓爆時到底做了什麼。

測試方法

測試在四個代管 Postgres 服務上跑同一支失控查詢:ClickHouse Managed Postgres、Google Cloud SQL、PlanetScale Postgres、Amazon RDS,全部統一在 Postgres 18.6、同等級機型(r8gd.large / db-c4a-highmem-2 / db.r8g.large)。查詢本體是一段遞迴 CTE,用 UNION(非 UNION ALL)的去重雜湊表製造出約 1260 萬個節點的圖,光是這張去重雜湊表就吃掉約 1 GiB 記憶體:

WITH RECURSIVE walk(n) AS (
  SELECT 0
  UNION
  SELECT (walk.n + step.s) % 12600000
  FROM walk
  CROSS JOIN (VALUES (1), (251)) AS step(s)
)
SELECT n FROM walk;

併發連線數從 7 一路加到 23,每個連線數重跑 10 次、跑完冷卻 60 秒,並設定 120 秒的 statement_timeout。失敗分三類記錄:查詢報錯、連線被砍斷、整個叢集當機。

結果與影響

服務防護機制觀察結果(23 連線)失敗時的訊息
ClickHouse Managed Postgres停用 kernel 記憶體 overcommit,對已提交記憶體設硬上限查詢完成率 32%,叢集存活率 100%SQLSTATE 53200(out_of_memory),交易回滾、連線不中斷
Amazon RDS無主動防護,仰賴 Linux OOM killer 事後補救查詢完成率 7%,叢集存活率 0%OOM killer 對行程送出 SIGKILL,Postgres 視為共享記憶體損毀,整叢集重啟進入 crash recovery
Google Cloud SQLSupervisor 搶在 OOM 前主動砍連線低到中負載就已出現不穩定FATAL: terminating connection due to administrator command
PlanetScale PostgresSupervisor 搶在 OOM 前主動砍連線11 個連線以上多數執行直接崩潰FATAL: terminating connection due to administrator command

三種失敗模式的差別在於誰先出手、出手後系統是否還完整。ClickHouse 自己在應用層擋下超額記憶體,回報明確的 SQLSTATE 錯誤,交易回滾但連線和叢集都還在;Cloud SQL、PlanetScale 的 supervisor 搶在真正 OOM 前砍連線,但只丟出一句「因管理指令終止連線」,不解釋原因;RDS 沒有中間防線,直接讓 Linux OOM killer 動手,而 Postgres 把行程被 SIGKILL 這件事解讀成共享記憶體損毀,觸發的是整個叢集重啟,不是單一查詢失敗。

ClickHouse 表示自己是四家中唯一同時做到「關閉 kernel overcommit」與「對已提交記憶體設硬上限」的服務:記憶體用量會在約 57% 的主機 RAM 就封頂(16 GiB 機型約 9 GiB),而不是像 RDS 那樣讓 backend 幾乎吃光全部可用 RAM;shared_buffers 預設就先佔掉主機 25%(4GB)。對正在管理受管 Postgres、或會寫分析型查詢與遞迴 CTE 的工程師來說,這代表評估供應商時不能只看「有沒有 statement_timeout」,還要問清楚記憶體被壓爆時,系統擋下的是單一連線,還是整台機器。

原始來源:Can your Postgres survive a bad query?(ClickHouse Blog)


End of article
0
Would love your thoughts, please comment.x
()
x