多年來,PostgreSQL 一直是數一數二的重要幕後資料系統,支援 ChatGPT 和 OpenAI API 等核心產品。隨著使用者群快速成長,對資料庫的需求也呈指數型增長。過去一年,我們的 PostgreSQL 負載成長超過 10 倍,且仍在快速上升。
為了推進生產基礎設施以維持這種成長,我們做出努力,並由此獲得新的見解:PostgreSQL 可以擴展,可靠地支援比先前許多人認為的要大得多的讀取密集型工作負載。該系統 (最初由加州大學柏克萊分校的一組科學家團隊建構) 讓我們支援龐大的全球流量,透過單一主要的 Azure PostgreSQL 靈活伺服器執行個體(在新視窗中開啟) 以及近 50 個分散於全球多個區域的讀取複本。這是我們在 OpenAI 如何擴展 PostgreSQL 的故事:透過嚴謹的最佳化和紮實的工程實作,支援 8 億使用者每秒數百萬次查詢。我們也將分享一路走來所學到的關鍵經驗教訓。
ChatGPT 推出後,流量以空前的速度成長。為了支援此功能,我們迅速在應用程式和 PostgreSQL 資料庫層面實施了廣泛的最佳化,並透過增加執行個體大小進行垂直擴展,以及透過增加更多讀取副本進行水平擴展。這套架構長久以來都為我們帶來卓越成效。隨著持續改良,它繼續為未來的成長提供充足的空間。
單一主架構能滿足 OpenAI 的規模需求,這或許令人驚訝;然而,在實務上做到這一點並不簡單。我們已觀察到多起由 Postgres 過載引起的 SEV,且它們通常遵循相同的模式:上游問題導致資料庫負載突然增加,例如快取層故障導致大範圍快取未命中、昂貴的多路連接激增使 CPU 飽和,或新功能發布引發寫入風暴。隨著資源使用率攀升,查詢延遲增加、要求也開始逾時。重試會進一步增加負載,觸發惡性循環,有可能降低整個 ChatGPT 和 API 服務的效能。
儘管 PostgreSQL 在處理以讀取為主的工作負載時擴展性良好,我們在寫入流量高峰期仍然面臨挑戰。這主要是由於 PostgreSQL 的多版本並發控制 (MVCC) 實作方式,使得它在寫入密集型工作負載下效率較低。例如,當查詢更新元組甚至單一欄位時,整列都會複製以建立新版本。在高寫入負載下,這會導致寫入顯著放大。這也會增加讀取放大效應,因為查詢必須掃描多個元組版本 (死元組) 才能擷取最新的版本。MVCC 帶來額外的挑戰,例如表格和索引膨脹、索引維護開銷增加,以及複雜的自動清理調整。(你可以在我與卡內基美隆大學 Andy Pavlo 教授合著的部落格文章《The Part of PostgreSQL We Hate the Most》(在新視窗中開啟)中找到這些問題的深入探討,該文章已由 PostgreSQL 維基百科頁面引用(在新視窗中開啟)。)
為了緩解這些限制並降低寫入壓力,我們已經移轉、並持續移轉可分片的 (即可水平分割的工作負載),將寫入密集型工作負載分配到分片系統 (例如 Azure Cosmos DB),並最佳化應用程式邏輯以盡量減少不必要的寫入。我們也不再允許在目前的 PostgreSQL 部署中新增表格。新的工作負載預設使用分片系統。
即使我們的基礎架構已經演進,PostgreSQL 仍維持未分片,由單一主要執行個體處理所有寫入。主要原因是,對現有應用程式工作負載進行分片將會非常複雜且耗時,需要變更數百個應用程式端點,可能需要數月甚至數年之久。由於我們的工作負載主要以讀取為主,且已實施廣泛的最佳化,目前的架構仍提供充足的餘裕,能支援持續的流量成長。雖然我們未來不排除對 PostgreSQL 進行分片,但考量到我們對今後的成長空間充裕,短期內這並非優先事項。
接下來的章節將深入探討我們所面臨的挑戰,以及為了解決這些問題並防範未來中斷而實施的廣泛最佳化措施,將 PostgreSQL 推向極限,並將其擴展到每秒數百萬次查詢 (QPS)。
挑戰:由於只有一個寫入器,單一主設定無法擴展寫入能力。大量寫入尖峰可能會迅速使主系統超載,並影響 ChatGPT 和我們的 API 等服務。
解決方案:我們盡可能將主節點的負載降到最低 (包括讀取與寫入) 以確保它有足夠的容量來應對寫入尖峰。讀取流量會在可能的情況下分流至副本。然而,某些讀取查詢必須保留在主節點上,因為它們是寫入交易的一部分。針對這些,我們著重確保效率,避免查詢緩慢。針對寫入流量,我們已將可分片、寫入密集的工作負載移轉至分片系統,例如 Azure CosmosDB。難以分片但仍但寫入量仍然很高的工作負載需要更久才能移轉,而該過程仍在進行中。我們也積極最佳化應用程式以減少寫入負載。例如,我們修正了導致重複寫入的應用程式錯誤,並在適當情況下引入延遲寫入,以平滑流量高峰。此外,在回填表格欄位時,我們會強制執行嚴格的限速,以防過度的寫入壓力。
挑戰:我們在 PostgreSQL 中識別出數個高成本的查詢。過去,這些查詢的突發流量會消耗大量 CPU 資源,導致 ChatGPT 和 API 要求速度變慢。
解決方案:少數高成本的查詢 (例如將多個表格連接在一起的查詢) 可能會顯著降低效能,甚至導致整個服務中斷。我們需要持續最佳化 PostgreSQL 查詢,以確保其效率,並避免常見的線上交易處理 (OLTP) 反模式。例如,我們曾發現一個成本極高的查詢,該查詢連接了 12 個表格,而該查詢中的峰值是過去嚴重 SEV 事件的罪魁禍首。我們應該盡量避免複雜的多表格連接。如果必須進行連接,我們學會了考慮分解查詢,並將複雜的連接邏輯移到應用程式層。許多這類有問題的查詢是由物件關聯對應框架 (ORM) 產生的,因此務必仔細審閱它們產生的 SQL,並確保其行為符合預期。在 PostgreSQL 中,長時間運行的閒置查詢也很常見。設定像 idle_in_transaction_session_timeout 這類的逾時參數至關重要,可避免它們阻塞 autovacuum。
挑戰:如果讀取副本發生故障,流量仍可路由到其他副本。然而,依賴單一寫手代表存在著單一故障點—如果它出現問題,整個服務都會受到影響。
解決方案:大多數關鍵要求僅涉及讀取查詢。為了減少主節點的單點故障風險,我們將讀取操作從寫入節點轉移到副本,確保即使主節點故障,這些要求仍能繼續提供服務。雖然寫入操作仍會失敗,但影響已降低;由於讀取仍然可用,因此不再是 SEV0 等級。
為了減少主要故障的影響,我們以高可用性 (HA) 模式運行主節點,並設置熱備援:即持續同步的副本,隨時準備接管流量。如果主節點發生故障或需要離線維護,我們可以迅速將備援節點提升為主節點,將停機時間降至最低。Azure PostgreSQL 團隊已投入大量工作,確保即使在極高負載下,這些容錯移轉仍能保持安全可靠。為了處理讀取副本故障,我們在每個區域部署多個副本,並保留足夠的容量餘裕,確保單一副本故障不會導致區域性中斷。
挑戰:我們經常遇到某些要求在 PostgreSQL 執行個體上消耗過多資源的情況。這可能會導致在相同執行個體上執行的其他工作負載效能下降。例如,推出新功能可能會引入效率不佳的查詢,進而大量消耗 PostgreSQL 的 CPU,導致其他關鍵功能的要求變慢。
解決方案:我們在多個地理區域運行近 50 個讀取副本,以盡量減少延遲。然而,在目前的架構下,主伺服器必須將 WAL 串流傳輸到每個副本。雖然目前它在處理極大執行個體類型和高網路頻寬方面的擴展性很好,但我們不能無限期地增加副本,否則最終會導致主節點超載。為了解決這個問題,我們正與 Azure PostgreSQL 團隊合作進行級聯複製(在新視窗中開啟),在此架構中,中繼副本會將 WAL 轉送至下游副本。這種方法可能有辦法讓我們擴展到逾一百個副本,而不會讓主節點不堪負荷。然而,這也帶來額外的操作複雜性,在容錯移轉管理方面特別如此。此功能仍在測試中;我們將確保其穩定可靠,並能在容錯移轉後安全運行,然後再投入生產環境。
挑戰:特定端點的突發流量高峰、昂貴查詢的激增,或重試風暴可能會迅速耗盡 CPU、I/O 和連線等關鍵資源,導致大範圍的服務效能下降。
解決方案:我們在多個層面實施速限,包括應用程式、連線集區管理器、代理伺服器和查詢,以防止突發流量高峰壓垮資料庫執行個體並引發連鎖故障。此外,也必須避免重試間隔過短,否則可能會引發重試風暴。我們也增強了 ORM 層的功能,以支援限速,並在必要時完全封鎖特定的查詢摘要。這種有針對性的限電方式能夠從高成本查詢的突然激增中快速恢復過來。
挑戰:即使是小型的結構描述變更,例如變更欄的類型,也可能會觸發整份表格重寫(在新視窗中開啟)。因此,我們謹慎進行結構描述變更,僅限於輕量級操作,並避免任何會重寫整份表格的變更。
解決方案:僅允許進行輕量級的結構描述變更,例如新增或移除不會觸發完整表格重寫的某些欄。我們對結構描述強制執行嚴格的 5 秒超時限制。允許同時建立及刪除索引。結構描述變更僅限於現有的表格。如果新功能需要額外的表格,則必須位於替代的分片系統,例如 Azure CosmosDB,而不是 PostgreSQL。在回填表格欄位時,我們會套用嚴格的速限以防止寫入高峰。雖然此流程有時可能需要超過一週,但它能確保穩定性並避免對生產造成任何影響。
這項努力表明,透過正確的設計和最佳化,Azure PostgreSQL 可以擴展以處理最大的生產工作負載。PostgreSQL 處理讀取密集型工作負載的每秒數百萬次查詢,為 OpenAI 最關鍵的產品 (如 ChatGPT 和 API 平台) 提供支援。。我們增加了將近 50 個讀取副本,將複寫延遲維持在接近零的水準,在地理分佈區域內保持低延遲讀取,並建立足夠的容量空間以支援未來的成長。
這種擴展方式在運作時,仍能將延遲降到最低並提高可靠性。我們在生產環境中穩定提供低雙位數毫秒的 p99 用戶端延遲,以及 99.999% 的可用性。而在過去 12 個月裡,我們只發生過一次 SEV-0 PostgreSQL 事件 (發生在 ChatGPT ImageGen 的 病毒式傳播(在新視窗中開啟)期間,當時因為一週內有超過 1 億名新使用者註冊,寫入流量突然暴增超過 10 倍。)
雖然我們對 PostgreSQL 所取得的成就感到滿意,但仍將繼續挑戰它的極限,以確保有足夠的空間來實現未來的成長。我們已將可分片的寫入密集型工作負載移轉到分片系統 (例如 CosmosDB)。其餘寫入密集型工作負載較難分片—我們也正積極將其移轉,以進一步減輕 PostgreSQL 主節點的寫入壓力。我們也正在與 Azure 合作,啟用級聯複寫,以便安全地擴展到顯著更多的讀取副本。
展望未來,隨著基礎架構需求的不斷增長,我們將繼續探索進一步擴展的其他方法,包括分片 PostgreSQL 或其他分散式系統。
作者
致謝
特別感謝 Jon Lee、Sicheng Liu、Chaomin Yu 和 Chenglong Hao 對本文的貢獻,以及協助擴展 PostgreSQL 的整個團隊。我們也要感謝 Azure PostgreSQL 團隊的鼎力合作。


