分散式 PostgreSQL - Citus 架構及概念

為少發表於2022-03-06

節點

Citus 是一種 PostgreSQL 擴充套件,它允許資料庫伺服器(稱為節點)在“無共享(shared nothing)”架構中相互協調。這些節點形成一個叢集,允許 PostgreSQL 儲存比單臺計算機上更多的資料和使用更多的 CPU 核心。 這種架構還允許通過簡單地向叢集新增更多節點來擴充套件資料庫。

Coordinator 與 Worker

每個 cluster 都有一個稱為 coordinator(協調器) 的特殊節點(其他節點稱為 worker 節點)。應用程式將它們的查詢傳送到 coordinator 節點,coordinator 節點將其轉發給相關的 worker 並累積結果。

對於每個查詢,coordinator 要麼將其路由到單個 worker 節點,要麼將其並行化到多個節點,具體取決於所需資料是位於單個節點上還是多個節點上。coordinator 通過查閱其後設資料表知道如何做到這一點。這些 Citus 特定表跟蹤 worker 節點的 DNS 名稱和執行狀況,以及跨節點資料的分佈情況。

分散式資料

表型別

Citus 叢集中有三種型別的表,每種表都以不同方式儲存在節點中,並且用於不同的目的。

型別 1:分散式表

第一種型別,也是最常見的,是分散式表。對於 SQL 語句而言,它們看似是普通的表,但在 worker 節點之間水平分割槽。

這裡 table 的行儲存在 worker 的表 table_1001table_1002 等中。 元件 worker 表稱為分片(shards)

分佈列

Citus 使用使用分片演算法將行分配到分片。基於表列(稱為分佈列(distribution column))的值執行分配,此分配具有確定性。叢集管理員在分佈表時必須指定此列。做出正確的選擇,這一點對於效能和功能有重要影響。

型別 2:引用表

引用表 是一種分散式表,其全部內容都集中到單個分片中,並在每個 worker 上覆制。因此,對任何 worker 的查詢都可以在本地訪問 引用 資訊,無需從另一個節點請求行,因此也不會產生此類網路開銷。引用表沒有分佈列,因為無需區分每行的各個分片。

引用表 通常很小,用於儲存與在任何工作節點上執行的查詢相關的資料。例如,訂單狀態或產品類別等列舉值。

當與 引用表 互動時,我們會自動對事務執行兩階段提交 (2PC)。 這意味著 Citus 確保您的資料始終處於一致狀態,無論您是在寫入修改還是刪除它。

型別 3:本地表

當您使用 Citus 時,您連線並與之互動的 coordinator 節點是安裝了 Citus 擴充套件的常規 PostgreSQL 資料庫。 因此,您可以建立普通表並選擇不對其進行分片。 這對於不參與連線查詢的小型管理表很有用。 一個示例是用於應用程式登入和身份驗證的使用者表。

建立標準 PostgreSQL 表很容易,因為它是預設值。這是你執行 CREATE TABLE 時得到的。在幾乎每個 Citus 部署中,我們都會看到標準 PostgreSQL 表與 distributedreference 表共存。事實上,如前所述,Citus 本身使用本地表來儲存叢集後設資料。

Shards

上一節將分片描述為在 worker 節點內的較小表中包含分散式表的行的子集。本節詳細介紹了技術細節。

協調器上的 pg_dist_shard 後設資料表包含系統中每個分散式表的每個分片的行。該行與分片 ID 相匹配,分片 ID 的範圍是一組雜湊整數 (shardminvalue, shardmaxvalue)。

SELECT * from pg_dist_shard;
 logicalrelid  | shardid | shardstorage | shardminvalue | shardmaxvalue
---------------+---------+--------------+---------------+---------------
 github_events |  102026 | t            | 268435456     | 402653183
 github_events |  102027 | t            | 402653184     | 536870911
 github_events |  102028 | t            | 536870912     | 671088639
 github_events |  102029 | t            | 671088640     | 805306367
 (4 rows)

如果 coordinator 節點要確定哪個分片包含 github_events 行,它將對行中分佈列的值執行雜湊演算法。然後此節點檢查哪個分片的範圍包含此雜湊值。 定義範圍後,雜湊函式的image(影像)就是兩者的並查。

分片放置

假設分片 102027 與相應的行關聯。在某個 worker 中的 github_events_102027 表中讀取或寫入此行。是哪個 worker?這完全由後設資料表確定。分片對映到 worker 的過程稱為分片放置(shard placement)

coordinator 節點將查詢重寫為引用特定表(例如 github_events_102027)的片段,並對相應 worker 執行這些片段。 下面的查詢示例在後臺執行,旨在查詢分片 ID102027 的節點。

SELECT
    shardid,
    node.nodename,
    node.nodeport
FROM pg_dist_placement placement
JOIN pg_dist_node node
  ON placement.groupid = node.groupid
 AND node.noderole = 'primary'::noderole
WHERE shardid = 102027;
┌─────────┬───────────┬──────────┐
│ shardid │ nodename  │ nodeport │
├─────────┼───────────┼──────────┤
│  102027 │ localhost │     5433 │
└─────────┴───────────┴──────────┘

github_events 示例中,有四個分片。每個表的分片數量在其在叢集中分佈時是可配置的。

最後請注意,Citus 允許複製分片以防止資料丟失。有兩種複製“模式”:Citus 複製和流複製。前者建立額外的備份分片放置並針對所有更新它們的所有它們執行查詢。後者效率更高,利用 PostgreSQL 的流式複製將每個節點的整個資料庫備份到一個 follower 資料庫。這是透明的,不需要 Citus 後設資料表的參與。

共置

由於可以根據需要將分片及其副本放置在節點上,因此將包含相關表的相關行的分片放在同一節點上是有意義的。 這樣,它們之間的連線查詢可以避免通過網路傳送儘可能多的資訊,並且可以在單個 Citus 節點內執行。

一個示例是包含商店、產品和購買的資料庫。如果所有三個表都包含 - 並且由 - store_id 列分佈,那麼限制在單個儲存中的所有查詢都可以在單個工作節點上高效執行。即使查詢涉及這些表的任意組合也是如此。

並行性

跨多臺機器分散查詢允許一次執行更多查詢,並允許通過向叢集新增新機器來擴充套件處理速度。此外,如上一節所述,將單個查詢拆分為片段可以提高專用於它的處理能力。 後一種情況實現了最大的並行性,這意味著 CPU 核心的利用率。

讀取或影響均勻分佈在多個節點上的分片的查詢能夠以“實時”速度執行。 請注意,查詢的結果仍然需要通過協調器節點傳回,因此當最終結果緊湊時(例如計數和描述性統計等聚合函式),加速效果最為明顯。

查詢執行

在執行多分片查詢時,Citus 必須平衡並行性的收益與資料庫連線的開銷(網路延遲和工作節點資源使用)。要配置 Citus 的查詢執行以獲得最佳的資料庫工作負載結果,它有助於瞭解 Citus 如何管理和儲存協調節點和工作節點之間的資料庫連線。

Citus 將每個傳入的多分片查詢會話轉換為稱為任務的每個分片查詢。 它將任務排隊,並在能夠獲得與相關工作節點的連線時執行它們。對於分散式表 foobar 的查詢,下面是連線管理圖:

coordinator 節點為每個會話都有一個連線池。每個查詢(例如圖中的 SELECT * FROM foo)僅限於為每個 worker 的任務開啟最多 citus.max_adaptive_executor_pool_size(整數)個同時連線。 該設定可在會話級別進行配置,以進行優先順序管理。

在同一連線上按順序執行短任務比為它們並行建立新連線更快。 另一方面,長時間執行的任務受益於更直接的並行性。

為了平衡短任務長任務的需求,Citus 使用 citus.executor_slow_start_interval(整數)。 該設定指定多分片查詢中任務的連線嘗試之間的延遲。 當查詢首先對任務進行排隊時,這些任務只能獲取一個連線。 在每個有待處理連線的時間間隔結束時,Citus 會增加它將開啟的同時連線數。通過將 GUC 設定為 0,可以完全禁用慢啟動行為。

當任務完成使用連線時,會話池將保持連線開啟以供以後使用。快取連線避免了 coordinatorworker 之間重新建立連線的開銷。但是,每個池一次開啟的空閒連線不超過 citus.max_cached_conns_per_worker(整數)個,以限制 worker 中空閒連線資源的使用。

最後,設定 citus.max_shared_pool_size (integer) 充當故障保險。它限制了所有任務之間每個 worker 的總連線數。

更多

相關文章