分享到

簡介

雖然開發和預演環境可以幫助您預測在生產環境中會遇到的許多狀況,但有些挑戰只會在規模擴大時才會開始浮現。資料庫連線管理就屬於這一類:來自用戶端實例的請求數量可能會迅速擴展,超出資料庫軟體支援的連線限制。

為了防止長時間佇列、請求失敗和影響使用者的錯誤,需要連線管理策略和工具來解決這種資源爭用問題。連線池化是一種策略,其基於部署中介佇列系統來管理和回收資料庫連線,通常可以成功地用於減輕這些問題。

在本指南中,我們將討論什麼是連線池化、它旨在解決哪些特定條件,以及它的運作方式。我們將介紹一些常見的實作方式作為代表性範例,並討論當用戶端請求超出資料庫的可用連線數時,它們如何改變用戶端的行為方式。

開啟資料庫連線涉及哪些步驟?

在我們一般性地討論連線管理和特別是連線池化之前,先仔細看看資料庫連線期間發生了什麼可能會有幫助。

為了讓用戶端應用程式開啟與資料庫的連線,必須執行許多令人驚訝的步驟。對於每個連線,必須執行以下部分或全部步驟

  • 找到資料庫伺服器的 IP 位址所需的任何 DNS 查找
  • 執行建立與伺服器的 TCP 連線所需的三向交握
  • 通過 TLS 交握 協商並啟用連線加密
  • 與資料庫軟體交換偏好設定和需求,以建立會話參數
  • 執行資料庫身份驗證檢查以建立用戶端的身份
  • 執行初始授權檢查以建立用戶端有權存取請求的資料庫物件
  • 執行實際查詢並傳回結果
  • 終止資料庫會話、TLS 加密和 TCP 連線

連線數量如何影響資料庫伺服器資源?

除了完成上述所有步驟所需的時間外,每個連線還需要資源來建立和維護。例如,在 PostgreSQL 中,一些測試的工作負載導致每個連線使用 1.5-14.5MB 的記憶體

隨著伺服器必須管理每個新連線的狀態,CPU 使用率也會隨著連線數量的增加而上升。隨著更多記憶體和 CPU 用於管理連線,它們可能會開始影響其他事物,例如 可以執行的交易速率。管理大量連線的額外負荷開始干擾資料庫系統最佳化快取結果的能力,並降低其執行有用工作的能力。

連線數量如何影響用戶端應用程式?

雖然以上段落描述了資料庫伺服器必須支付的連線成本,但也直接影響了用戶端。資料庫伺服器只能配置為接受一定數量的連線。當達到該限制時,將拒絕其他連線請求。

這表示,預設情況下,您的用戶端程式碼需要實作邏輯來使用指數退避演算法重複請求,以處理這些失敗。然而,更重要的是,您的用戶端可能被迫頻繁使用該功能,導致查詢停滯、延遲以及可能迅速向上傳遞給使用者的問題。

在沒有中介中間組件的情況下,您將被迫考慮

  • 提高資料庫伺服器的連線限制(影響記憶體和 CPU 使用率以及資料庫伺服器的交易速率),
  • 擴展您的資料庫以分配更多記憶體、CPU 或網路容量,或
  • 向外擴展您的資料庫,以將請求分佈到更多機器上

這些選擇本身不一定是負面的,但對於我們在此描述的壅塞類型來說可能過度。連線池化是另一種選擇,可以幫助您用現有資源做更多事情。

什麼是連線池?

連線池化是一種策略,涉及為多個請求回收資料庫連線,而不是在查詢已解決後立即關閉它們。通常,這是通過在資料庫伺服器及其用戶端應用程式之間引入一個稱為連線池管理器的軟體來完成的,該軟體負責管理兩者之間的連線。

如前所述,在形成連線時,在資料庫伺服器實際執行查詢之前,必須執行相當長的一系列操作。連線池管理器試圖通過在初始查詢後保持連線開啟並重複使用它來執行其他查詢,從而攤銷這些操作的成本。

在這個系統中,用戶端連線到連線池管理器,而不是直接連線到資料庫。用戶端將池管理器視為資料庫本身,而池管理器解釋查詢,以便將適當的連線交給用戶端。需要注意的一件事是,開啟和關閉用戶端與池管理器之間的連線仍然涉及額外負荷。然而,這些連線通常具有較低的額外負荷,因為大部分繁重的程序發生在建立與資料庫本身的連線時。

連線池如何運作?

連線池管理器負責代表用戶端開啟、關閉和維護與資料庫的連線。它通過遵循類似於快取系統可能使用的演算法來做到這一點。

當用戶端連線到連線池管理器並請求連線時,池管理器會快速評估請求特徵。它可能會查看資料庫使用者、將執行的特定操作、加密類型或存取的資料庫物件等資訊。

一旦它獲得了這些資訊,它就會查看其可用連線池,以查看是否有任何現有連線可以用來執行新請求。如果它找到合適、可用的連線,它會將其交給用戶端,並允許用戶端通過連線執行其查詢。如果池中不存在適合執行新請求的連線,它將使用所需的參數開啟與資料庫的新連線,並將其交給用戶端。

用戶端像往常一樣使用連線執行其查詢。當查詢完成時,池管理器不會終止連線,而是將連線放回池中,以便後續查詢可以重複使用它。池管理器可以使用它選擇的任何演算法(自建立以來的時間、自上次使用以來的時間等)在其池中非同步地垃圾收集連線。

內部與外部池化有何差異?

廣義來說,連線池化指的是維護多個請求過程中的連線的演算法。這可以在用戶端應用程式內部實作,或在外部使用外部工具或服務實作。

連線池化的內部實作通常是資料庫驅動程式、ORM(物件關聯對應器)或其他可能整合到用戶端應用程式中的資料庫用戶端的功能。這些解決方案通過維護與資料庫伺服器的長時間運作連線並在程式碼庫中重複使用它們來進行多個查詢,從而提供連線池化的一些優點。

雖然內部連線池化很有用,但它確實有一些實際限制。正在執行的每個應用程式實例通常必須維護自己的池。這會影響連線在查詢之間可以共享和重複使用的廣度,因為每個池僅服務於單個應用程式實例。

另一種解決方案是實作外部連線池化。這種方法部署了一個單獨的軟體,可以與多個用戶端實例通信並池化連線。雖然這種部署情境確實引入了額外的網路躍點,但它通常提供額外的可擴展性和靈活性。連線池管理器可以與資料庫伺服器一起部署,以服務於許多不同的用戶端,或者與用戶端應用程式一起部署,以服務於在單個伺服器上運行的任何應用程式實例。

常見的外部連線池有哪些?

有許多適用於不同資料庫系統的連線池管理器。我們可以查看一些適用於 PostgreSQL 的實作,以更好地了解不同解決方案如何處理問題。

pgbouncer

對於 PostgreSQL 來說,也許最知名的連線池管理器是 pgbouncer

pgbouncer 創建於 2007 年,專注於為管理 PostgreSQL 連線提供輕量級池化機制。無論是在部署位置方面還是在執行池化的具體方式方面,它都提供了很大的靈活性。

對於來自用戶端的連線生命週期短暫的情況,該專案建議在用戶端程式碼將執行的 Web 伺服器上部署 `pgbouncer`。將池管理器與用戶端軟體放在一起,可以使兩者之間的連線使用比 TCP 更輕量的機制,從而減少延遲。對於需要將來自許多不同用戶端的連線池化在一起的情境,您可以改為與資料庫伺服器一起部署。

使用 `pgbouncer` 時最重要的決定之一是選擇您希望使用的池化模式。三個可用的選項是

  • 交易池化:用戶端在每次交易後撤銷連線。對於任何後續交易,將再次分配連線。這允許 `pgbouncer` 在用戶端可能在交易之間執行其他操作時快速回收連線。
  • 會話池化:連線在用戶端與池管理器的連線期間分配給用戶端。這表示每個用戶端連線都與資料庫的專用連線配對。一旦用戶端會話結束,連線仍會重複使用,但可以同時使用池管理器的用戶端數量大大減少。
  • 語句池化:連線分配用於執行個別語句。這會導致連線的快速分配和解除分配,這允許許多用戶端使用有限數量的連線,但在某些情況下可能會破壞交易語義並導致意外行為。

在大多數情況下,交易池化在回收閒置連線、管理相當數量的用戶端以及維護關於資料庫會話和交易語義的預期行為方面提供了最佳平衡。

pgpool

另一個 PostgreSQL 連線池管理器是 `pgpool-II`,通常簡稱為 `pgpool`。雖然 `pgbouncer` 是一個專注於連線池化的輕量級工具,但 `pgpool` 提供了更多相關功能的選擇。

除了連線池化之外,`pgpool` 還支援在多個後端資料庫實例之間負載平衡查詢,並具有監控服務,以實現高可用性操作以進行自動故障轉移。此外,它還提供了一個管理 GUI,對於某些部署情境可能非常有用。

儘管有這些進階功能,但在實際管理連線池化方面,`pgpool` 通常被認為有點受限。雖然 `pgbouncer` 允許三種池化模式,但 `pgpool` 只能以相當於會話模式的模式運作,這表示僅在用戶端斷線時重新分配連線。相比之下,這減少了 `pgpool` 可以處理的用戶端數量。

結論

在本指南中,我們了解了連線池化的概念,以及如何使用它來幫助減少資料庫負載和資源消耗。我們概述了用戶端和資料庫之間建立連線可能很昂貴的一些原因,並描述了重複使用連線如何減少後續查詢的成本。之後,我們討論了連線池管理器實際上是如何運作的,並了解了來自 PostgreSQL 生態系統的一些池管理器的代表性範例。

隨著應用程式擴展且查詢負載變得更加複雜,資料庫連線限制可能會快速引起問題。雖然無法完全消除與連線管理相關的額外負荷,但連線池管理器是用於維持效能和增加資料庫可以服務的用戶端數量的寶貴工具。

關於作者
Justin Ellingwood

Justin Ellingwood

Justin 自 2013 年以來一直撰寫關於資料庫、Linux、基礎設施和開發人員工具的文章。他目前與妻子和兩隻兔子住在柏林。他通常不必以第三人稱寫作,這讓所有相關方都鬆了一口氣。