分享至

簡介

雖然開發和預演環境可以幫助您預測在生產中將面臨的許多情況,但有些挑戰只有在規模擴大後才會開始顯現。資料庫連線管理就屬於這一範疇:來自客戶端例項的請求數量可能很快超出資料庫軟體支援的連線限制。

需要連線管理策略和工具來解決這種資源爭用,以防止長時間的排隊、失敗的請求和影響使用者的錯誤。**連線池**是一種圍繞部署中間排隊系統來管理和回收資料庫連線的策略,通常被成功地用於緩解這些問題。

在本指南中,我們將討論什麼是連線池,它旨在解決哪些特定問題,以及它是如何工作的。我們將介紹一些流行的實現作為代表性示例,並討論當客戶端請求超出資料庫可用連線時,它們如何改變客戶端的行為。

開啟資料庫連線涉及哪些操作?

在泛泛地談論連線管理和具體地談論連線池之前,仔細研究資料庫連線過程中發生的事情可能會有所幫助。

對於客戶端應用程式來說,開啟一個數據庫連線,需要執行令人驚訝的許多步驟。對於每個連線,必須執行以下部分或全部步驟:

  • 定位資料庫伺服器 IP 地址所需的任何 DNS 查詢
  • 執行建立到伺服器的 TCP 連線所需的三次握手
  • 透過TLS 握手協商並啟用連線加密
  • 與資料庫軟體交換偏好和要求以建立會話引數
  • 執行資料庫身份驗證檢查以確定客戶端身份
  • 執行初始授權檢查以確定客戶端是否可以訪問請求的資料庫物件
  • 執行實際查詢並返回結果
  • 關閉資料庫會話、TLS 加密和 TCP 連線

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

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

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

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

儘管上一段描述了資料庫伺服器必須支付的連線成本,但連線對客戶端也有直接影響。資料庫伺服器只能配置為接受特定數量的連線。當達到該限制時,額外的連線請求將被拒絕。

這意味著,預設情況下,您的客戶端程式碼需要實現邏輯以透過指數退避演算法重複請求來處理這些失敗。然而,更重要的是,您的客戶端可能被迫頻繁使用該功能,從而導致查詢停滯、延遲以及可能迅速波及到使用者的問題。

如果沒有一箇中間調解元件,您將被迫考慮:

  • 增加資料庫伺服器的連線限制(影響資料庫伺服器的記憶體和 CPU 使用率以及事務速率),
  • 擴充套件資料庫以分配更多記憶體、CPU 或網路容量,或
  • 擴充套件資料庫以將請求分佈到更多機器上

這些選擇本身不一定是負面的,但對於我們在此描述的擁塞型別來說,它們可能顯得過度。連線池是一種替代方案,它有助於您利用現有資源做更多事情。

什麼是連線池?

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

如前所述,在建立連線時,需要執行相當長的一系列操作,然後資料庫伺服器才能實際執行查詢。連線池器試圖透過在初始查詢後保持連線開啟並重用它來執行其他查詢來分攤這些操作的成本。

在此係統中,客戶端連線到連線池器而不是直接連線到資料庫。客戶端將池器視為資料庫本身,池器解釋查詢以將適當的連線交給客戶端。需要注意的是,客戶端和池器之間開啟和關閉連線仍然存在開銷。然而,這些連線通常開銷較低,因為大部分繁重過程發生在建立與資料庫本身的連線時。

連線池如何工作?

連線池器負責代表客戶端開啟、關閉和維護到資料庫的連線。它透過遵循類似於快取系統可能使用的演算法來實現。

當客戶端連線到連線池器並請求連線時,池器會快速評估請求特徵。它可能會檢視諸如資料庫使用者、將執行的具體操作、加密型別或訪問的資料庫物件等資訊。

一旦獲得這些資訊,它會檢視其可用連線池,以檢視是否有任何現有連線可用於執行新請求。如果找到合適的可用連線,它會將其交給客戶端,並允許客戶端透過該連線執行其查詢。如果池中不存在適合執行新請求的連線,它將使用所需引數開啟一個新連線到資料庫,並將其交給客戶端。

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

內部池化和外部池化有什麼區別?

廣義而言,連線池是指在多個請求過程中維護連線的演算法。這可以在客戶端應用程式內部實現,也可以使用外部工具或服務進行外部實現。

連線池的內部實現通常是資料庫驅動程式、ORM(物件關係對映器)或其他可能整合到客戶端應用程式中的資料庫客戶端的功能。這些解決方案透過維護到資料庫伺服器的長期執行連線並將其用於程式碼庫中的多個查詢來提供連線池的一些好處。

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

另一種解決方案是實現外部連線池。這種方法部署了一個獨立的軟體,可以為多個客戶端例項進行通訊和池化連線。雖然這種部署場景確實引入了額外的網路跳數,但它通常提供了額外的可擴充套件性和靈活性。連線池器可以與資料庫伺服器一起部署以服務許多不同的客戶端,也可以與客戶端應用程式一起部署以服務在單個伺服器上執行的任何應用程式例項。

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

有許多適用於不同資料庫系統的連線池。我們可以透過檢視一些適用於 PostgreSQL 的實現,來更好地理解不同的解決方案如何處理這個問題。

pgbouncer

也許 PostgreSQL 最著名的連線池是 pgbouncer

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

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

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

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

在大多數情況下,事務池化在回收空閒連線、管理合理數量的客戶端以及維護資料庫會話和事務語義方面的預期行為方面提供了最佳平衡。

pgpool

另一個 PostgreSQL 連線池是 pgpool-II,通常簡稱為 pgpool。雖然 pgbouncer 是一個專門用於連線池的輕量級工具,但 pgpool 提供了更廣泛的相關功能選擇。

除了連線池,pgpool 還支援在多個後端資料庫例項之間進行查詢負載均衡,並擁有一個看門狗服務,以實現自動故障轉移的高可用性操作。此外,它還提供了一個管理 GUI,這對於某些部署場景非常有用。

儘管具有這些高階功能,但 pgpool 在實際管理連線池方面通常被認為功能有限。雖然 pgbouncer 允許三種池化模式,但 pgpool 只能以相當於會話模式的方式執行,這意味著只有在客戶端斷開連線時,連線才會被重新分配。這降低了 pgpool 可以處理的客戶端數量。

結論

在本指南中,我們探討了連線池的概念以及如何利用它來幫助減少資料庫負載和資源消耗。我們概述了連線在客戶端和資料庫之間建立成本高昂的一些原因,並描述了重用連線如何降低後續查詢的成本。之後,我們討論了連線池器實際的工作原理,並查看了 PostgreSQL 生態系統中一些代表性的池器示例。

隨著應用程式規模的擴大和查詢負載變得更加複雜,資料庫連線限制可能會迅速引發問題。雖然完全消除與連線管理相關的開銷是不可能的,但連線池器是維護效能和增加資料庫可服務客戶端數量的寶貴工具。

關於作者
Justin Ellingwood

Justin Ellingwood

Justin 自 2013 年以來一直撰寫有關資料庫、Linux、基礎設施和開發者工具的文章。他目前與妻子和兩隻兔子住在柏林。他通常不必用第三人稱寫作,這對所有相關方來說都是一種解脫。
© . This site is unofficial and not affiliated with Prisma Data, Inc.