1. 程式人生 > >在 GitLab 我們是如何擴展數據庫的

在 GitLab 我們是如何擴展數據庫的

有關 blog exe clu 開始 board 設置 服務器 mon

很長時間以來 GitLab.com 使用了一個單個的 PostgreSQL 數據庫服務器和一個用於災難恢復的單個復制。在 GitLab.com 最初的幾年,它工作的還是很好的,但是隨著時間的推移,我們看到這種設置的很多問題,例如,數據庫長久處於重壓之下, CPU 使用率幾乎所有時間都處於 70% 左右。

在我們使用 PostgreSQL 去跟蹤這些問題時,使用了以下的四種技術:

1、優化你的應用程序代碼,以使查詢更加高效。

2、使用一個連接池去減少必需的數據庫連接數量及相關的資源。

3、跨多個數據庫服務器去平衡負載。

4、分片數據庫。

連接池

在 PostgreSQL 中,一個連接是通過啟動一個操作系統進程來處理的,這反過來又需要大量的資源,更多的連接(及這些進程)將使用你的數據庫上的更多的資源。 PostgreSQL 也在 max_connections 設置中定義了一個強制的最大連接數量。一旦達到這個限制,PostgreSQL 將拒絕新的連接, 比如,下面的圖表示的設置:

技術分享圖片

這裏我們的客戶端直接連接到 PostgreSQL,這樣每個客戶端請求一個連接。

通過連接池,我們可以有多個客戶端側的連接重復使用一個 PostgreSQL 連接。例如,沒有連接池時,我們需要 100 個 PostgreSQL 連接去處理 100 個客戶端連接;使用連接池後,我們僅需要 10 個,或者依據我們配置的 PostgreSQL 連接。這意味著我們的連接圖表將變成下面看到的那樣:

技術分享圖片

這裏我們展示了一個示例,四個客戶端連接到 pgbouncer,但不是使用了四個 PostgreSQL 連接,而是僅需要兩個。

對於 PostgreSQL 有兩個最常用的連接池:

  • pgbouncer
  • pgpool-II

pgpool 有一點特殊,因為它不僅僅是連接池:它有一個內置的查詢緩存機制,可以跨多個數據庫負載均衡、管理復制等等。另一個 pgbouncer 是很簡單的:它就是一個連接池。

數據庫負載均衡

數據庫級的負載均衡一般是使用 PostgreSQL 的 “熱備機hot-standby” 特性來實現的。 熱備機是允許你去運行只讀 SQL 查詢的 PostgreSQL 副本,與不允許運行任何 SQL 查詢的普通備用機standby相反。要使用負載均衡,你需要設置一個或多個熱備服務器,並且以某些方式去平衡這些跨主機的只讀查詢,同時將其它操作發送到主服務器上。擴展這樣的一個設置是很容易的:(如果需要的話)簡單地增加多個熱備機以增加只讀流量。

這種方法的另一個好處是擁有一個更具彈性的數據庫集群。即使主服務器出現問題,僅使用次級服務器也可以繼續處理 Web 請求;當然,如果這些請求最終使用主服務器,你可能仍然會遇到錯誤。

然而,這種方法很難實現。例如,一旦它們包含寫操作,事務顯然需要在主服務器上運行。此外,在寫操作完成之後,我們希望繼續使用主服務器一會兒,因為在使用異步復制的時候,熱備機服務器上可能還沒有這些更改。

分片

分片是水平分割你的數據的行為。這意味著數據保存在特定的服務器上並且使用一個分片鍵檢索。例如,你可以按項目分片數據並且使用項目 ID 做為分片鍵。當你的寫負載很高時,分片數據庫是很有用的(除了一個多主設置外,均衡寫操作沒有其它的簡單方法),或者當你有大量的數據並且你不再使用傳統方式保存它也是有用的(比如,你不能把它簡單地全部放進一個單個磁盤中)。

不幸的是,設置分片數據庫是一個任務量很大的過程,甚至,在我們使用諸如 Citus 的軟件時也是這樣。你不僅需要設置基礎設施 (不同的復雜程序取決於是你運行在你自己的數據中心還是托管主機的解決方案),你還得需要調整你的應用程序中很大的一部分去支持分片。

GitLab 的連接池

對於連接池我們有兩個主要的訴求:

1、它必須工作的很好(很顯然這是必需的)。

2、它必須易於在我們的 Omnibus 包中運用,以便於我們的用戶也可以從連接池中得到好處。

用下面兩步去評估這兩個解決方案(pgpool 和 pgbouncer):

1、執行各種技術測試(是否有效,配置是否容易,等等)。

2、找出使用這個解決方案的其它用戶的經驗,他們遇到了什麽問題?怎麽去解決的?等等。

pgpool 是我們考察的第一個解決方案,主要是因為它提供的很多特性看起來很有吸引力。我們其中的一些測試數據可以在 這裏 找到。

最終,基於多個因素,我們決定不使用 pgpool 。例如, pgpool 不支持粘連接sticky connection。 當執行一個寫入並(嘗試)立即顯示結果時,它會出現問題。想像一下,創建一個工單issue並立即重定向到這個頁面, 沒有想到會出現 HTTP 404,這是因為任何用於只讀查詢的服務器還沒有收到數據。針對這種情況的一種解決辦法是使用同步復制,但這會給表帶來更多的其它問題,而我們希望避免這些問題。

另一個問題是, pgpool 的負載均衡邏輯與你的應用程序是不相幹的,是通過解析 SQL 查詢並將它們發送到正確的服務器。因為這發生在你的應用程序之外,你幾乎無法控制查詢運行在哪裏。這實際上對某些人也可能是有好處的, 因為你不需要額外的應用程序邏輯。但是,它也妨礙了你在需要的情況下調整路由邏輯。

由於配置選項非常多,配置 pgpool 也是很困難的。或許促使我們最終決定不使用它的原因是我們從過去使用過它的那些人中得到的反饋。即使是在大多數的案例都不是很詳細的情況下,我們收到的反饋對 pgpool 通常都持有負面的觀點。雖然出現的報怨大多數都與早期版本的 pgpool 有關,但仍然讓我們懷疑使用它是否是個正確的選擇。

結合上面描述的問題和反饋,最終我們決定不使用 pgpool 而是使用 pgbouncer 。我們用 pgbouncer 執行了一套類似的測試,並且對它的結果是非常滿意的。它非常容易配置(而且一開始不需要很多的配置),運用相對容易,僅專註於連接池(而且它真的很好),而且沒有明顯的負載開銷(如果有的話)。也許我唯一的報怨是,pgbouncer 的網站有點難以導航。

使用 pgbouncer 後,通過使用事務池transaction pooling我們可以將活動的 PostgreSQL 連接數從幾百個降到僅 10 - 20 個。我們選擇事務池是因為 Rails 數據庫連接是持久的。這個設置中,使用會話池session pooling不能讓我們降低 PostgreSQL 連接數,從而受益(如果有的話)。通過使用事務池,我們可以調低 PostgreSQL 的 max_connections 的設置值,從 3000 (這個特定值的原因我們也不清楚) 到 300 。這樣配置的 pgbouncer ,即使在尖峰時,我們也僅需要 200 個連接,這為我們提供了一些額外連接的空間,如 psql 控制臺和維護任務。

對於使用事務池的負面影響方面,你不能使用預處理語句,因為 PREPAREEXECUTE 命令也許最終在不同的連接中運行,從而產生錯誤的結果。 幸運的是,當我們禁用了預處理語句時,並沒有測量到任何響應時間的增加,但是我們 確定 測量到在我們的數據庫服務器上內存使用減少了大約 20 GB。

為確保我們的 web 請求和後臺作業都有可用連接,我們設置了兩個獨立的池: 一個有 150 個連接的後臺進程連接池,和一個有 50 個連接的 web 請求連接池。對於 web 連接需要的請求,我們很少超過 20 個,但是,對於後臺進程,由於在 GitLab.com 上後臺運行著大量的進程,我們的尖峰值可以很容易達到 100 個連接。

今天,我們提供 pgbouncer 作為 GitLab EE 高可用包的一部分。對於更多的信息,你可以參考 “Omnibus GitLab PostgreSQL High Availability”。

整合連接池和數據庫負載均衡

整合連接池和數據庫負載均衡可以讓我們去大幅減少運行數據庫集群所需要的資源和在分發到熱備機上的負載。例如,以前我們的主服務器 CPU 使用率一直徘徊在 70%,現在它一般在 10% 到 20% 之間,而我們的兩臺熱備機服務器則大部分時間在 20% 左右:

技術分享圖片

CPU Percentage

在這裏, db3.cluster.gitlab.com 是我們的主服務器,而其它的兩臺是我們的次級服務器。

其它的負載相關的因素,如平均負載、磁盤使用、內存使用也大為改善。例如,主服務器現在的平均負載幾乎不會超過 10,而不像以前它一直徘徊在 20 左右:

技術分享圖片

CPU Percentage

在業務繁忙期間,我們的次級服務器每秒事務數在 12000 左右(大約為每分鐘 740000),而主服務器每秒事務數在 6000 左右(大約每分鐘 340000):

技術分享圖片

Transactions Per Second

可惜的是,在部署 pgbouncer 和我們的數據庫負載均衡器之前,我們沒有關於事務速率的任何數據。

我們的 PostgreSQL 的最新統計數據的摘要可以在我們的 public Grafana dashboard 上找到。

我們的其中一些 pgbouncer 的設置如下:

設置
default_pool_size 100
reserve_pool_size 5
reserve_pool_timeout 3
max_client_conn 2048
pool_mode transaction
server_idle_timeout 30

除了前面所說的這些外,還有一些工作要作,比如: 部署服務發現(#2042), 持續改善如何檢查次級服務器是否可用(#2866),和忽略落後於主服務器太多的次級服務器 (#2197)。

值得一提的是,到目前為止,我們還沒有任何計劃將我們的負載均衡解決方案,獨立打包成一個你可以在 GitLab 之外使用的庫,相反,我們的重點是為 GitLab EE 提供一個可靠的負載均衡解決方案。

如果你對它感興趣,並喜歡使用數據庫、改善應用程序性能、給 GitLab上增加數據庫相關的特性(比如: 服務發現),你一定要去查看一下我們的 招聘職位 和 數據庫專家手冊 去獲取更多信息。

參考網址:https://linux.cn/article-9048-1.html

在 GitLab 我們是如何擴展數據庫的