從 ChatGPT 的 PostgreSQL 最佳實踐學到的事:規模靠的不是神話,是紀律
最近看到 OpenAI 分享 ChatGPT 如何用 PostgreSQL 服務到「8 億用戶」等級的實務做法。
這種文章常被當成巨頭逸聞:看完覺得很猛,但跟自己無關。
我反而覺得,真正值得抄的不是規模,而是背後的工程紀律:當你把資料庫當成產品核心的一部分來經營,你會怎麼設計它的邊界、觀測、與失敗模式。
下面把九個重點拆成三層來看:
- 這 9 點在 ChatGPT 裡實際長什麼樣
- 為什麼它們特別適合「AI 時代」的 DB 使用方式
- 如果你是 PM + Python side project / 小型 SaaS,怎麼做縮小版
## 這 9 點在 ChatGPT 裡實際長什麼樣
### 1)單一 writer + 大量 read replicas
ChatGPT 的核心是 Azure 上單一 PostgreSQL primary,外面掛了大約 50 個跨區域 read replica。
系統把絕大多數查詢導向 replicas,只把跟寫入同一個 transaction 綁定的查詢留在 primary。這讓 primary 更像「一致性與交易的控制塔」,而讀取吞吐量交給 replica 水平擴展。
中小團隊可落地版
- 先從「一主一從」開始也行,先把路徑切出來。
- 把「讀歷史、讀設定、讀報表」這些查詢抽象成明確的 read-only repository/service,之後才有機會無痛切到 replica。
### 2)嚴格監控高成本查詢(特別針對 ORM)
他們特別點名 ORM 會生成又長又慢的 SQL,所以在 DB 層與 ORM 層都有慢查詢監控與封鎖機制;甚至能直接針對特定 query digest 做 block。
這背後的核心心態很明確:讓「看似方便」的查詢成本變得可見、可控、可禁止。
中小團隊可落地版
- 在 Python / FastAPI 層把 ORM 產生的 SQL 全部 log 下來(帶 request id、route、user、feature tag)。
- 做一張慢查詢週報:Top N 最慢、Top N 最常出現、Top N 最耗總時間。
- 必要時針對某些 pattern 做限流或直接拒絕。
### 3)單 writer 也要 HA:把只讀關鍵路徑搬去 replicas
即便只有一個寫入節點,也會用複本做熱備援。
更重要的是:把「只讀關鍵路徑」搬到 replicas。這樣 primary 出問題時,讀流量仍然有機會繼續被服務,產品不會立刻全黑。
中小團隊可落地版
- 不要只做備援而不演練。定期做一次「如果 primary 掛了,我們還能提供哪些功能」的桌上推演。
- 先把登入、權限、設定、讀歷史這些路徑變成可以在 read-only 模式下提供的能力。
### 4)Workload 分級與隔離:避免 noisy neighbor
高優先級(例如關鍵查設定、核心 metadata)的查詢,與次要工作(分析、報表、後台批次)分散到不同節點。
這避免「一個 noisy neighbor 把整個叢集拖垮」的經典事故。
中小團隊可落地版
- 先從最簡單的隔離做起:報表、批次任務走獨立資料庫或至少獨立 read replica。
- 在應用層做 queue,把非即時工作改成背景處理。
### 5)PgBouncer 代理層:用 transaction/statement pooling 壓住連線數
在應用與 Postgres 之間插 PgBouncer,用 statement/transaction pooling 大幅減少實際 DB 連線數。
分享裡提到連線建立時間從約 50ms 壓到 5ms,跨區延遲場景特別有感。
中小團隊可落地版
- PgBouncer 幾乎可以視為標配:Docker compose 直接加一層。
- 先用 transaction pooling 把連線暴增問題壓住,再來談其他優化。
### 6)Cache miss 的「單一讀取者」:dogpile prevention
遇到多層快取都 miss 時,不讓所有請求一起回源打爆 DB。
只允許一個請求去資料庫補快取,其他請求等待結果(典型 request coalescing / singleflight)。
中小團隊可落地版
- 在應用層加一個 per-key lock(Redis lock 或單機 mutex 都行),把同一個 key 的回源合併。
- 對熱門 key 設定更短 TTL + 背景刷新,避免同一時間一起過期。
### 7)WAL 中介副本(研發中):降低 primary 的複寫扇出
他們提到一個演進方向:未來希望不是所有 replicas 都直接從 primary 拉 WAL。
先複製到中介 replica,再由中介分發給多個 read replicas,降低 primary 的 WAL 傳送負載與複製扇出壓力。
中小團隊可落地版
- 這招多半用不到,但思路很值得記:primary 要做的事越少越好。
- 你可以先在架構上避免把所有附加功能都掛在 primary 上(例如所有查詢都走 primary、所有報表都走 primary)。
### 8)多層級 Rate limit:避免 retry storm,必要時精準 shed load
他們在 application、PgBouncer、proxy,甚至 query 層都做 rate limiting。
同時避免太頻繁重試造成 retry storm;必要時能針對昂貴 query 的 digest 做精準的 shed load。
中小團隊可落地版
- 在 gateway / API 層做 per-user、per-key、per-route 的 rate limit。
- 把重試策略寫死:指數退避、上限次數、加 jitter。
- 對「回源 DB」的路徑設更嚴的限流。
### 9)Schema 變更極度保守:DDL 5 秒 timeout、索引一律 concurrent
他們的 schema 變更政策非常克制:
- 只允許輕量 DDL
- 禁止會引發 full table rewrite 的變更
- DDL 設 5 秒 timeout
- 索引一律用 concurrent 建/刪
- 新功能若需要更可水平擴展的寫入,會把新表丟去可 sharding 的系統(例如 Cosmos DB),而不是硬塞回主 Postgres
- 需要 backfill 時會嚴格限流,寧可跑一週也不要把線上打爆
中小團隊可落地版
- 設一份 migration 操作守則:線上不 drop column、不改型別、不做可能 rewrite 全表的操作。
- 索引一律 concurrently,backfill 一律小 batch + sleep。
- 把 migration 視為一次「線上操作」,需要 SLO、需要回滾方案。
## 為什麼這些特別適合「AI 時代」的 DB 使用方式
### AI 產品常見的型態:超 read-heavy + 少量關鍵 write
AI 產品大量讀:讀對話、讀設定、讀 usage metadata。
寫入存在,但往往是少量且關鍵(帳務、狀態、使用量、權限)。
這讓「大量 replicas 承接讀、primary 專注寫與一致性」變成非常符合直覺的配置。
### 流量尖峰更不可預測:保護 critical path 才是重點
模型側很容易突然造成尖峰:新模型上線、某國家湧入、某種 prompt pattern 爆炸式擴散。
多層 rate limit + workload isolation 的價值在於:
- 讓核心路徑(帳務、權限、核心存取)不會被實驗性功能拖下水
- 在壓力下仍能提供可用但降級的服務
### 演進式路線:RDB 當核心,把新 workloads 外移到可水平擴展系統
以 RDB 作為核心,再把寫得很兇或需要水平擴展的新 workloads 外移到 sharded/NoSQL(例如 Cosmos DB),對產品團隊而言是一條演進式路線。
你不需要一次性重寫所有東西去換 DB,而是逐步把不適合的負載搬出去。
## 如果你是 PM + Python side project / 小型 SaaS:縮小版怎麼做
我會照同樣精神做一個「不奢華但耐用」的配置:
- 早點把讀寫分離的路徑切出來:就算一開始沒有 replica,也先把 code 結構準備好。
- 把 ORM 成本做成可觀測性:SQL logging + tag + 慢查詢報表。
- 把 PgBouncer 當成標配:先解決連線暴增,再談其他。
- 對 burst 先從 API 層擋掉:per-user / per-key / per-route rate limit。
- 把 migration 當線上操作來規範:concurrent index、DDL timeout、backfill 限流。
你不需要 8 億用戶才開始做這些。
你需要的是在你還有餘裕的時候,先把「會在未來變成事故的地方」變成一份清單,並且逐步把它們系統化。
#PostgreSQL #資料庫 #SRE #架構設計 #效能優化 #ChatGPT