試駕 Citus 11.0 beta

為少發表於2022-05-17

image

https://www.citusdata.com/blog/2022/03/26/test-drive-citus-11-beta-for-postgres/

Citus 11.0 beta 的最大變化是 schema 和 Citus 後設資料現在在整個資料庫叢集中自動同步。這意味著您始終可以從 Citus 叢集中的任何節點查詢分散式表!

使用 Citus 最簡單的方法是連線到協調器節點並將其用於 schema 更改和分散式查詢,但是對於要求非常高的應用程式,您現在可以選擇通過使用不同的連線字串並考慮一些限制,在應用程式(部分)的工作節點之間對分散式查詢進行負載平衡。

我們也在 11.0 beta 版中棄用了一些特性來加速我們的開發,但我們希望這不會影響到你們中的大多數人。

在這篇 11.0 beta 版部落格文章中,您將瞭解:

  • Citus 11.0 beta 中新的自動後設資料同步功能
  • 如何配置 Citus 11.0 beta 叢集
  • 如何跨工作節點負載平衡查詢
  • 升級到 11.0 beta 版
  • 改進的叢集活動檢視
  • 事務塊中的後設資料同步
  • 棄用

您可以試用新的 Citus 11.0 beta ,看看您的應用程式將如何使用它,或者嘗試新功能。 您可以在我們的安裝說明中找到這些軟體包。

我們剛剛推出了 11.0 beta 的新型發行說明,如果您想深入瞭解我們的開源 GitHub 儲存庫並檢視我們在此版本中解決的問題,這應該很有用。 如果您覺得它有用,請在 Slack 上告訴我們!我們也計劃為即將釋出的 Citus 版本釋出此類版本說明。您可以通過 Citus 網站頂部導航中的 “UPDATES” 連結找到這些發行說明。

自動後設資料同步允許您從任何節點查詢

Citus 可能是擴充套件 PostgreSQL 資料庫的最佳方式。當您分發表時,Citus 可以跨大型 PostgreSQL 伺服器叢集路由和並行化複雜查詢。除了初始設定之外,分發對應用程式是透明的:您的應用程式仍然連線到單個 PostgreSQL 節點(Citus 用語中的“協調器”),並且協調器在後臺分發您的應用程式傳送的 Postgres 查詢。

image
圖 1:Citus 10.2 或更早版本中的 Citus 叢集,其中使用者和專案是分散式表,它們的後設資料僅在協調器上。

單協調器架構有很多好處並且效能非常好,但是對於某些高效能工作負載,協調器可能會成為瓶頸。在實踐中,很少有應用程式會遇到協調器的瓶頸,因為 Citus 協調器所做的工作相對較少。但是,我們確實發現應用程式開發人員通常希望在可擴充套件性方面為未來做好準備,並且有一些要求非常高的企業應用程式。

很長一段時間以來,Citus 通過同步分散式表 schema 和後設資料,能夠通過工作節點執行分散式查詢。過去,我們有時將此功能稱為“MX”。 但是,MX 功能在使用序列(sequences)、函式(functions)、模式(schemas)和其他資料庫物件方面存在各種限制——這意味著並非所有表都支援後設資料同步。

Citus 11.0 beta 更改為新的操作模式:現在所有 Citus 資料庫叢集始終使用後設資料同步。這意味著使用 Citus 11.0 beta 和所有未來版本,您始終可以從任何節點執行分散式 Postgres 查詢。

image
圖 2:Citus 11.0 beta 叢集,其中 users 和 items 是分散式表,並且使用新的自動後設資料同步功能,它們的後設資料會同步到所有節點。

當您開始使用 Citus 11.0 beta 時,您無需執行任何操作來啟用新的後設資料同步功能。 每個分散式表、資料庫物件和 schema 更改都將自動傳播到所有 Citus worker 節點。 Schema 更改和節點管理仍然需要傳送到 Citus 協調器,您可以通過更改應用程式中的連線字串來選擇將分散式 Postgres 查詢傳送到協調器或任何其他節點。

如何配置 Citus 11.0 beta 叢集

如果您需要在 PostgreSQL 資料庫上每秒執行許多查詢,則可能需要使用相對大量的連線。 最終,您的總吞吐量在 [連線數]/[平均響應時間],因為您一次只能對每個連線進行一次查詢。

當您的應用程式開啟與其中一個 Citus 節點的連線時,該連線會產生一個 Postgres 程式。 這個 Postgres 程式需要與其他節點建立內部連線以查詢分散式表的分片。這些內部連線被快取以最小化響應時間。這確實意味著來自客戶端的每個連線最終都會導致與其他節點的額外內部連線,因此每個節點最終將獲得與客戶端對整個資料庫叢集的連線數。幸運的是,我們在 PostgreSQL 14 中對連線可擴充套件性進行了重大改進,允許 Postgres(和 Citus)在高連線數下保持良好的效能。

如果您決定從應用程式連線到 worker 節點以執行分散式查詢,那麼您的客戶端連線在技術上與內部連線競爭。為了確保處理客戶端連線的每個 Postgres 程式也可以與所有其他節點建立內部連線,我們新增了 citus.max_client_connections 設定。此設定限制外部客戶端連線的數量,同時繼續允許 Citus 節點之間的內部連線。除了通常的安裝說明外,我們建議在每個 Citus 節點(協調器和所有工作器)上的 postgresql.conf 中新增以下設定,以適應大量客戶端連線:

# The maximum number of client + internal connections a node can handle
# The total number of client connections across all nodes should never exceed this number
max_connections = 6000

# The number of client connections an individual node can handle
# Should be no greater than: max_connections / node count including the coordinator
citus.max_client_connections = 500

使用這些設定,每個節點將接受來自您的應用程式的多達 500 個連線,因此如果您有 10 個工作節點和 1 個協調器,那麼您的應用程式總共可以建立 5500 個連線。 您可以通過在每個節點上使用像 pgbouncer 這樣的連線池來進一步增加這個數字。

我們還強烈建議將 Citus 協調器新增到後設資料中,以便工作節點也可以連線到協調器。 僅當協調器在後設資料中時,某些 Citus 功能才可用。我們可能會在未來新增所需的協調器。

-- on all nodes:
CREATE EXTENSION citus;
-- only on coordinator: add coordinator to metadata
SELECT citus_set_coordinator_host('<coordinator’s own hostname>', 5432);
-- only on coordinator: add worker nodes to metadata
SELECT citus_add_node('<worker 1 hostname>', 5432);
SELECT citus_add_node('<worker 2 hostname>', 5432);
-- only on coordinator:
CREATE TABLE items (key text, value text);
SELECT create_distributed_table('items', 'key');
-- from any node:
INSERT INTO items VALUES ('hello', 'world');

Citus 11.0 beta 中跨工作節點的負載平衡查詢

Citus 11.0 beta 叢集啟動並執行後,您有 2 個選擇:

  • 您可以像往常一樣將您的應用程式連線到協調器,或者
  • 通過使用支援負載平衡的客戶端和自定義連線字串(如 JDBCNpgsql),在工作節點之間對 Postgres 查詢進行負載均衡。 您還應該能夠在已經使用這些客戶端之一的現有應用程式中進行負載均衡。

在 2 個 worker 之間進行負載平衡的示例 JDBC 連線字串

jdbc:postgresql://user@worker1.host:5432,worker2.host:5432/postgres?loadBalanceHosts=true

在 2 個 worker 之間進行負載均衡的示例 Npgsql 連線字串

Host=worker1.host,worker2.host;Database=postgres;Username=user;Load Balance Hosts=true

另一種方法是設定一個包含所有工作節點 IP 的 DNS 記錄。 使用 DNS 的一個缺點是,由於本地 DNS 快取,來自同一臺機器的同時開啟的連線通常會使用相同的 IP。 另一種選擇是設定一個專用的負載均衡器,如 HAProxy

在 11.0 beta 通過 Citus 工作節點執行 Postgres 查詢時,需要注意一些限制:

  • 您需要配置您的應用程式以通過 Citus 協調器執行 schema 更改,而查詢可以通過任何節點進行。
  • 如果您在一個工作節點上建立表,如果您隨後連線到不同的工作節點,它將不會顯示。
  • 如果您啟用 citus.use_citus_managed_tables 設定或建立引用表的外來鍵,則協調器上的本地表僅出現在 worker 節點上。
  • 生成 bigint 的序列將在序列號的前 16 位包含所連線節點的 ID,這意味著序列號仍然是唯一的,但不是單調的。
  • 嘗試從工作節點插入時,生成 int/smallint 的序列會丟擲錯誤

我們希望在未來的 Citus 版本中解決上述限制。

將現有 Citus 資料庫叢集升級到 Citus 11.0 beta

如果您要將現有(非生產)叢集升級到 Citus 11.0 beta,那麼在安裝新軟體包後,您需要呼叫一個函式來完成升級:

-- on all nodes
ALTER EXTENSION citus UPDATE;

-- only on the coordinator
select citus_finalize_upgrade_to_citus11();

升級功能將確保所有工作節點都具有正確的 schema 和 metadata。 它還解決了影響分割槽表分片的幾個命名問題。

如果存在任何阻止後設資料同步的情況(例如,工作節點上缺少許可權或存在衝突的物件),則升級功能將丟擲錯誤。在解決問題並完成升級之前,您仍然可以通過 coordinator 使用現有的 Citus 資料庫群集,但一些新的 11.0 beta 功能將不可用。

叢集洞察的新檢視

Citus 經常要求的一項功能是更好地瞭解資料庫叢集中正在發生的事情。 當一些查詢通過 worker 節點進入時,這變得更加重要。

我們改進了 citus_dist_stat_activity 檢視以顯示來自所有節點上所有客戶端會話的 pg_stat_activity 的資訊,以及一個 global_pid(或 gpid),它唯一地標識一個客戶端會話和與該會話關聯的所有內部連線。gpid 以發起查詢的節點的節點 ID 開頭,即客戶端連線的節點。

SELECT nodeid, global_pid, query FROM citus_dist_stat_activity where application_name = 'psql';
┌────────┬─────────────┬────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ nodeid │ global_pid  │ state  │                                                 query                                                  │
├────────┼─────────────┼────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│      1 │ 10000001303 │ active │ SELECT nodeid, global_pid, state, query FROM citus_dist_stat_activity where application_name = 'psql'; │
│      2 │ 20000001346 │ active │ select count(*), pg_sleep(300) from test;                                                              │
└────────┴─────────────┴────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────┘

如果要取消特定查詢,只需將 global_pid 傳遞給 pg_cancel_backend。這適用於任何節點。

SELECT pg_cancel_backend(20000001346);

您還可以使用新的 citus_stat_activity 檢視檢視叢集中發生的所有事情(分散式查詢和內部查詢):

SELECT nodeid, global_pid, state, query, is_worker_query FROM citus_stat_activity WHERE global_pid = 20000001500;
┌────────┬─────────────┬────────┬──────────────────────────────────────────────────────────────────────────────────────────────────┬─────────────────┐
│ nodeid │ global_pid  │ state  │                                              query                                               │ is_worker_query │
├────────┼─────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┼─────────────────┤
│      2 │ 20000001500 │ active │ select count(pg_sleep(300)) from test;                                                           │ f               │
│      2 │ 20000001500 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102153 test WHERE true │ t               │
│      2 │ 20000001500 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102155 test WHERE true │ t               │
│      3 │ 20000001500 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102156 test WHERE true │ t               │
│      3 │ 20000001500 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102154 test WHERE true │ t               │
└────────┴─────────────┴────────┴──────────────────────────────────────────────────────────────────────────────────────────────────┴─────────────────┘

如果您正在使用 pg_stat_activity 檢視特定節點,您現在還可以在 application_name 中找到 worker 查詢所屬的 gpid

select pid, application_name, state, query from pg_stat_activity where query like '%count%' and application_name <> 'psql';
┌──────┬─────────────────────────────────┬────────┬──────────────────────────────────────────────────────────────────────────────────────────────────┐
│ pid  │        application_name         │ state  │                                              query                                               │
├──────┼─────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 1548 │ citus_internal gpid=10000001547 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102153 test WHERE true │
│ 1550 │ citus_internal gpid=10000001547 │ active │ SELECT count(pg_sleep('300'::double precision)) AS count FROM public.test_102155 test WHERE true │
└──────┴─────────────────────────────────┴────────┴──────────────────────────────────────────────────────────────────────────────────────────────────┘

由於每個節點都需要能夠連線到 Citus 叢集中的每個其他節點,因此我們還引入了一個新的健康檢查功能,用於檢查所有可能路徑的連通性。 結果列指示連線嘗試是否成功。

select * from citus_check_cluster_node_health();
┌───────────────┬───────────────┬─────────────┬─────────────┬────────┐
│ from_nodename │ from_nodeport │ to_nodename │ to_nodeport │ result │
├───────────────┼───────────────┼─────────────┼─────────────┼────────┤
│ localhost     │          1400 │ localhost   │        1400 │ t      │
│ localhost     │          1400 │ localhost   │        1401 │ t      │
│ localhost     │          1400 │ localhost   │        1402 │ t      │
│ localhost     │          1401 │ localhost   │        1400 │ t      │
│ localhost     │          1401 │ localhost   │        1401 │ t      │
│ localhost     │          1401 │ localhost   │        1402 │ t      │
│ localhost     │          1402 │ localhost   │        1400 │ t      │
│ localhost     │          1402 │ localhost   │        1401 │ t      │
│ localhost     │          1402 │ localhost   │        1402 │ t      │
└───────────────┴───────────────┴─────────────┴─────────────┴────────┘
(9 rows)

使用這些功能,即使您通過協調器執行所有查詢,您也應該對叢集中發生的事情有更好的瞭解。

在事務塊中嚴格、即時的後設資料同步

在分散式資料庫中,我們經常需要在一致性、容錯性、並行性和其他分散式系統方面進行權衡。Citus 需要支援 PostgreSQL 的互動式多語句事務塊,這在分散式環境中尤其具有挑戰性。

例如,Citus 通常跨分片並行化昂貴的操作 — 例如分析查詢和 create_distributed_table() 在每個 worker 的多個連線上。建立資料庫物件時,Citus 通過每個 worker 的單個連線將其傳播到 worker 節點。 在單個多語句事務中組合這兩個操作可能會導致問題,因為並行連線將無法看到通過單個連線建立但尚未提交的物件。

考慮一個建立型別、表、載入資料和分發表的事務塊:

BEGIN;
-- type creation over a single connection:
CREATE TYPE coordinates AS (x int, y int);
CREATE TABLE positions (object_id text primary key, position coordinates);
-- data loading thus goes over a single connection:
SELECT create_distributed_table(‘positions’, ‘object_id’);
\COPY positions FROM ‘positions.csv’
…

在 Citus 11.0 beta 之前,Citus 將推遲在 worker 節點上建立型別,並在執行 create_distributed_table 時單獨提交。這使得 create_distributed_table 中的資料複製能夠並行發生。然而,這也意味著該型別並不總是出現在 Citus worker 節點上 — 或者如果事務回滾,它只會出現在 worker 節點上。 我們能夠隱藏這些不一致之處,但最終它們可能會導致問題。

在 Citus 11.0 beta 中,預設行為更改為優先考慮協調器和工作節點之間的 schema 一致性。 這確實有一個缺點:如果物件傳播發生在同一事務中的並行命令之後,則該事務將無法再完成,如下面程式碼塊中的 ERROR 突出顯示:

BEGIN;
CREATE TABLE items (key text, value text);
-- parallel data loading:
SELECT create_distributed_table(‘items’, ‘key’);
\COPY items FROM ‘items.csv’
CREATE TYPE coordinates AS (x int, y int);
ERROR:  cannot run type command because there was a parallel operation on a distributed table in the transaction

如果您遇到此問題,有 2 個簡單的解決方法:

  1. 使用 set citus.create_object_propagation to deferred; 返回舊的物件傳播行為,在這種情況下,不同節點上存在哪些資料庫物件之間可能存在一些不一致。
  2. 使用 set citus.multi_shard_modify_mode to sequential 來禁用每個節點的並行性。 同一事務中的資料載入可能會更慢。

基於語句的分片複製更改

早在 2016 年,我們就宣佈棄用基於語句的分片複製以實現高可用性 (HA),轉而支援流式複製。當您在 Azure Database for PostgreSQL 上啟用Hyperscale (Citus) 的高可用性時,每個節點都將具有熱備用 - 這意味著該節點上的所有分片都通過流複製進行復制。 即使您不啟用高可用性,資料也會由託管磁碟在內部複製,以防止任何資料丟失。

儘管已棄用,但我們從未刪除基於語句的複製……它仍然可以用於在特定場景中擴充套件讀取,但是,已棄用的 HA 相關邏輯經常會導致問題,並阻止我們為複製表實現後設資料同步。 因此,作為 Citus 11.0 測試版的一部分,我們將行為更改如下:

在 Citus 11.0 測試版之前,當複製分片的寫入在其中一個分片位置上失敗時,Citus 將該位置標記為無效 - 之後必須重新複製分片。這個特性從來沒有很好地工作,因為零星的寫入失敗可能會使放置無效並導致昂貴的(寫入阻塞)重新複製。

從 Citus 11.0 beta 開始,對複製分片的寫入始終使用 2PC — 這意味著它們只有在所有放置都已啟動時才能成功。 此外,複製表的後設資料是同步的,因此可以從任何節點查詢它們。

今天使用基於語句的分片複製的開源使用者可以升級到 Citus 11.0 測試版——但是,當持有一個副本的節點發生故障時,要繼續接受對分片的寫入,應該通過 citus_disable_node 函式禁用故障節點。在替換或重新啟用節點後,仍然可以使用 replicate_table_shards 重新複製分片。

如果要使用基於語句的複製來擴充套件讀取吞吐量,則需要:

  • 在建立分散式表之前將 citus.shard_replication_factor 設定為 2,並且
  • citus.task_assignment_policy 設定為“round-robin(迴圈)”以在副本之間負載均衡查詢。

使用基於語句的複製來擴充套件讀取吞吐量的缺點是寫入具有更高的響應時間,並且更新和刪除被序列化以保持副本同步。

棄用:告別很少使用的功能

與 PostgreSQL 一樣,Citus 保持長期的向後相容性。 我們竭盡全力確保您的應用程式在升級 Citus 時繼續工作。 但是,有時某個功能不再符合 Citus 的使用方式並妨礙了開發。 我們決定在 11.0 測試版中刪除一些 Citus 功能:

  • 無效的分片放置:如上一節所述,當寫入失敗時,分片不再被標記為無效,因為這種行為在使用基於語句的複製時存在一些缺陷並降低了可靠性。
  • 追加分散式表函式:Citus 中最初的分發方法是“append(追加)”分發,它針對僅追加資料進行了優化。 Hash-distributed 表更容易使用並且具有更多功能,並且還可以通過分割槽很好地處理僅附加資料。Citus 11.0 beta 刪除了用於建立分片和將新資料載入到附加分散式表中的功能。 我們不知道有任何附加分散式表使用者,但以防萬一:您仍然可以升級到 11.0 beta,但這些表將變為只讀。 我們建議建立使用預設 hash-distribution 的新分散式表,並使用 INSERT .. SELECT 命令移動資料。
  • 分散式 cstore_fdw 表(應切換到列訪問方式):從 10.0 版開始,Citus 帶有內建的列式儲存。 在 Citus 10.0 之前,可以使用現已棄用的 cstore_fdw 擴充套件將 Citus 與列儲存一起使用。但是,cstore_fdw 不支援流式複製和備份等重要的 PostgreSQL 功能,因此在 Citus 10 之前我們很少看到 Citus 客戶使用列儲存。許多公司現在成功地使用 Citus 的內建列儲存來儲存時間序列資料,因此我們放棄了對建立或使用分散式 cstore_fdw 表的支援。如果您已分發 cstore_fdw 表,我們建議在升級到 11.0 beta 之前將它們轉換為列訪問方法。

藉助 Citus 11.0 beta 邁向新水平的可擴充套件性

Citus 是唯一一個完全作為 PostgreSQL 擴充套件實現的事務和分析工作負載的分散式資料庫,這意味著 Citus 大規模支援 PostgreSQL 的強大功能,並繼承了 PostgreSQL 的穩定性、效能、多功能性、可擴充套件性,以及龐大的工具生態系統和 擴充套件。

藉助 Citus 開源 11.0 測試版中的自動後設資料同步功能,您現在可以選擇從任何節點查詢您的 Citus 叢集,從而進一步提高 Citus 的可擴充套件性。

如果您有興趣試用新的 Citus 11.0 beta,您可以在 Citus 文件中找到 beta 版的安裝說明。安裝 Citus 後,入門頁面上有很多關於如何入門的好內容,包括教程和視訊。 最後,如果您想了解更多有關 Citus 內部工作原理的資訊,請檢視我們的 SIGMOD 論文

更多

探索 Python/Django 支援分散式多租戶資料庫,如 Postgres+Citus

相關文章