Citus on Ubuntu — PostgreSQLを水平分散させるシャーディング拡張

データベース

PostgreSQLのデータ量が数億行を超えたとき、単一サーバーでは追いつかなくなります。Citusは PostgreSQL をそのまま水平スケールさせる拡張機能で、テーブルを複数のシャードに分割して並列処理できるようになります。SQL の書き方はほぼ変わらず、普通の PostgreSQL として使い続けながら分散データベースの恩恵を受けられるのが特徴です。

この記事では Ubuntu 22.04 LTS 上で PostgreSQL 16 + Citus 14.1 を実際にインストールし、分散テーブルを作成してシャーディングが動いている様子を実測で確認しました。シングルサーバーの開発環境から始めて、本番の複数ノード構成に移行するときの基礎として役立ててください。

この記事のポイント

  • Ubuntu 22.04 で postgresql-16-citus-14.1 をインストールし citus_version() で Citus 14.1.0 を実測確認(PostgreSQL 16.14)
  • shared_preload_libraries = 'citus' を postgresql.conf に追加し、再起動してから CREATE EXTENSION citus
  • シングルノード開発環境では citus_set_node_property(..., 'shouldhaveshards', true) が必須。これを忘れるとシャードが 0 のまま無言で終わる
  • create_distributed_table('orders', 'user_id') 一行でシャードキーを指定できる。実行計画に Custom Scan (Citus Adaptive) と出れば分散クエリが動いている証拠
  • Citusリポジトリは PostgreSQL 15 / 16 / 17 に対応したパッケージを提供している

目次

  1. 前提環境と確認事項
  2. Citusリポジトリの追加
  3. PostgreSQL 16 + Citus 14.1 のインストール
  4. postgresql.conf の設定変更
  5. Citus拡張の有効化とバージョン確認
  6. シングルノードセットアップ(shouldhaveshards)
  7. 分散テーブルの作成とシャーディングデモ
  8. よくあるエラーと解決策
  9. まとめ

前提環境と確認事項

今回の検証環境は以下のとおりです。インストールと citus_version() の確認は Ubuntu 22.04 LTS(Docker ubuntu:22.04)で実施しました。シャーディングの SQL デモは citusdata/citus:14.0 の公式 Docker イメージを使って実測しています。

Citusリポジトリの提供パッケージ一覧(apt-cache policy 実行結果)
Citusリポジトリの提供パッケージ一覧(apt-cache policy 実行結果)
確認項目
OS Ubuntu 22.04 LTS (Jammy Jellyfish)
PostgreSQL 16.14(postgresql.org リポジトリから)
Citus 14.1.0(14.1.0.citus-1)
確認方法 Docker ubuntu:22.04 で全コマンドを実行して stdout を取得

注意

VPS や専用サーバーで作業する場合は sudo を先頭につけて実行してください。またCitusをインストールする前に PostgreSQL が動いていない状態から始めることをおすすめします。すでにPostgreSQLを使っているサーバーに追加する場合は、バージョンの整合性を必ず確認してください。

STEP 1: Citusリポジトリの追加

Citus は Ubuntu 標準のリポジトリには含まれていません。Citus Data(現 Microsoft)が管理する apt リポジトリを追加するところから始めます。




ubuntu@linuxlab: ~
$ sudo apt-get update
$ sudo apt-get install -y curl gnupg2 ca-certificates
$ curl -s https://install.citusdata.com/community/deb.sh | sudo bash
Adding repository for Citus on Ubuntu 22.04 (jammy)…
Adding deb https://repos.citusdata.com/community/ubuntu jammy main
Importing Citus GPG key…
Done.

install.citusdata.com/community/deb.sh は apt のリポジトリ設定と GPG 鍵を自動で追加するスクリプトです。Ubuntu のバージョンを自動検出して適切なリポジトリを設定します。

STEP 2: PostgreSQL 16 + Citus 14.1 のインストール

リポジトリを追加したら apt-get update で一覧を更新してからインストールします。インストール時にPostgreSQLが依存関係として自動的に入るため、事前に別途インストールしておく必要はありません。




ubuntu@linuxlab: ~
$ sudo apt-get update
$ sudo apt-get install -y postgresql-16-citus-14.1
Get:1 … postgresql-16 16.14-1.pgdg22.04+1 [18.2 MB]
Get:2 … postgresql-16-citus-14.1 14.1.0.citus-1
Setting up postgresql-16 (16.14-1.pgdg22.04+1) …
Creating new PostgreSQL cluster 16/main …
Setting up postgresql-16-citus-14.1 (14.1.0.citus-1) …
apt install の実ログ(Ubuntu 22.04、実測)
apt install の実ログ(Ubuntu 22.04、実測)

インストール後にパッケージが正しく入ったかを確認します。私の環境では以下のように表示されました。




ubuntu@linuxlab: ~
$ dpkg -l | grep -E “(postgresql|citus)”
ii postgresql-16 16.14-1.pgdg22.04+1
ii postgresql-16-citus-14.1 14.1.0.citus-1
ii postgresql-client-16 16.14-1.pgdg22.04+1
ii postgresql-client-common 291.pgdg22.04+1
ii postgresql-common 291.pgdg22.04+1
インストール済みパッケージ一覧(実測)
インストール済みパッケージ一覧(実測)

STEP 3: postgresql.conf の設定変更

Citus を有効化するには postgresql.confshared_preload_libraries = 'citus' を追加し、PostgreSQL を再起動する必要があります。 この設定を忘れると CREATE EXTENSION citus を実行したときにエラーになります。




ubuntu@linuxlab: ~
$ sudo sed -i “s/#shared_preload_libraries = ”/shared_preload_libraries = ‘citus’/” \
/etc/postgresql/16/main/postgresql.conf
$ grep “shared_preload_libraries” /etc/postgresql/16/main/postgresql.conf
shared_preload_libraries = ‘citus’ # (change requires restart)
$ sudo systemctl restart postgresql
$ sudo systemctl status postgresql
● postgresql.service – PostgreSQL RDBMS
Loaded: loaded (/lib/systemd/system/postgresql.service; enabled)
Active: active (running)

もし Nginx や他のミドルウェアと同居している場合、再起動で既存の接続が一瞬切れます。メンテナンス時間帯に作業するか、pg_reload_conf() でリロードできないか確認してください(ただし shared_preload_libraries の変更は再起動必須です)。

STEP 4: Citus拡張の有効化とバージョン確認

PostgreSQL を再起動したら、psql で接続して Citus 拡張を作成します。




ubuntu@linuxlab: ~
$ sudo -u postgres psql
psql (16.14 (Ubuntu 16.14-1.pgdg22.04+1))
Type “help” for help.
postgres=# CREATE EXTENSION citus;
CREATE EXTENSION
postgres=# SELECT citus_version();
citus_version
———————————————————————-
Citus 14.1.0 on x86_64-pc-linux-gnu, compiled by gcc 11.4.0, 64-bit
(1 row)

CREATE EXTENSION と表示されれば Citus の有効化は成功です。ここまで約5分で完了しました(Docker コンテナでの計測で、ネットワーク速度による)。

インストール〜拡張有効化までの実際のターミナル出力
インストール〜拡張有効化までの実際のターミナル出力

STEP 5: シングルノードセットアップ(shouldhaveshards の設定)

以降の SQL デモについて

STEP 5 以降のシャーディングデモは、公式 citusdata/citus:14.0 Docker イメージ(Citus 14.0.0)を使って実測しています。Ubuntu 22.04 に apt でインストールした Citus 14.1.0 でも同じ SQL と手順が動作します。

ここが詰まりやすいポイントです。Citus をインストールしただけでは分散テーブルのシャードが作られません。シングルサーバーで使うときは「このコーディネーターにもシャードを置いてよい」という設定を明示する必要があります。




postgres=# (psql)
— コーディネーターのホスト・ポートを登録
postgres=# SELECT citus_set_coordinator_host(‘localhost’, 5432);
citus_set_coordinator_host
—————————-
(1 row)

— シングルノード: コーディネーター自身にシャードを配置できるよう設定
postgres=# SELECT citus_set_node_property(‘localhost’, 5432, ‘shouldhaveshards’, true);
citus_set_node_property
————————-
(1 row)

— 設定を確認
postgres=# SELECT nodeid, nodename, nodeport, isactive, shouldhaveshards
FROM pg_dist_node;
nodeid | nodename | nodeport | isactive | shouldhaveshards
——–+———–+———-+———-+—————–
1 | localhost | 5432 | t | t
(1 row)

なぜこの設定が必要なのか

Citusはデフォルトで「コーディネーターノードにはシャードを置かない」という設計になっています。本番の複数ノード構成では、コーディネーターはクエリの調停役に徹してワーカーノードにデータを分散させます。シングルノードで開発するときだけ、shouldhaveshards = true でコーディネーター自身をシャード配置先として許可する必要があります。

STEP 6: 分散テーブルの作成とシャーディングデモ

設定が整ったら、実際に分散テーブルを作ってシャーディングが動くことを確認します。ECサイトの注文テーブルを例に使います。

①テーブルを作成して分散テーブルに変換する




postgres=# (psql)
— 通常のテーブルを作成(DDL は普通の PostgreSQL と同じ)
postgres=# CREATE TABLE orders (
order_id BIGSERIAL,
user_id BIGINT NOT NULL,
product_id INTEGER NOT NULL,
amount NUMERIC(10,2),
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE TABLE

— user_id をシャードキーとして 8 シャードに分散
postgres=# SELECT create_distributed_table(‘orders’, ‘user_id’, shard_count => 8);
create_distributed_table
————————–
(1 row)

create_distributed_table の第2引数がシャードキーです。ここに user_id を指定すると、同じユーザーの注文は必ず同じシャードに入ります。範囲クエリや集計クエリがユーザー単位なら、このキー選定が理想的です。

②シャードが作られたことを確認する




postgres=# (psql)
postgres=# SELECT s.shardid, sp.nodename, sp.nodeport, sp.shardstate
FROM pg_dist_shard s
JOIN pg_dist_shard_placement sp USING (shardid)
WHERE s.logicalrelid = ‘orders’::regclass
ORDER BY s.shardid;
shardid | nodename | nodeport | shardstate
———+———–+———-+————
102008 | localhost | 5432 | 1
102009 | localhost | 5432 | 1
102010 | localhost | 5432 | 1
102011 | localhost | 5432 | 1
102012 | localhost | 5432 | 1
102013 | localhost | 5432 | 1
102014 | localhost | 5432 | 1
102015 | localhost | 5432 | 1
(8 rows)
シャード配置確認 SQL の実行結果(実測)
シャード配置確認 SQL の実行結果(実測)

シャードID 102008102015 の8つが localhost:5432 に配置されました。shardstate = 1 は「有効(Active)」を意味します。本番環境で複数ワーカーノードがある場合は、各行の nodename/nodeport が異なるサーバーになります。

③データを挿入して分散クエリを確認する




postgres=# (psql)
— 10,000 件のテストデータを挿入
postgres=# INSERT INTO orders (user_id, product_id, amount)
SELECT (random()*999+1)::bigint,
(random()*99+1)::int,
(random()*4999+1)::numeric(10,2)
FROM generate_series(1,10000);
INSERT 0 10000

— ユーザー別の購入合計(分散集計クエリ)
postgres=# SELECT user_id, SUM(amount) AS total_spent, COUNT(*) AS order_count
FROM orders GROUP BY user_id ORDER BY total_spent DESC LIMIT 5;
user_id | total_spent | order_count
———+————-+————-
231 | 65202.49 | 21
563 | 54495.58 | 20
545 | 53507.20 | 19
942 | 52775.15 | 17
86 | 52233.87 | 16
(5 rows)

④実行計画で分散クエリを確認する

EXPLAIN で確認すると、Citusが分散クエリとして処理していることが見えます。




postgres=# (psql)
postgres=# EXPLAIN (COSTS off)
SELECT user_id, SUM(amount) FROM orders
WHERE user_id BETWEEN 100 AND 200 GROUP BY user_id;
QUERY PLAN
—————————————————————————
Custom Scan (Citus Adaptive)
Task Count: 8
Tasks Shown: One of 8
-> Task
Node: host=localhost port=5432 dbname=postgres
-> GroupAggregate
Group Key: user_id
-> Sort
-> Seq Scan on orders_102008 orders
(11 rows)
EXPLAIN による分散クエリ実行計画(実測)
EXPLAIN による分散クエリ実行計画(実測)

Custom Scan (Citus Adaptive)Task Count: 8 が出ていれば、Citus が 8 つのシャードに並列でクエリを投げています。スキャン対象が orders ではなく orders_102008(シャードテーブル)になっている点も確認ポイントです。

シャーディングSQLデモの実行結果(ターミナル出力)
シャーディングSQLデモの実行結果(ターミナル出力)

よくあるエラーと解決策

①ERROR: Citus extension needs to be loaded — shared_preload_libraries




postgres=# (psql)
ERROR: could not open extension control file
DETAIL: Citus extension needs to be loaded via shared_preload_libraries.

shared_preload_libraries = 'citus'postgresql.conf に追加してから sudo systemctl restart postgresql を実行していないケースです。設定を確認してから再起動してください。

②シャード数が 0 のまま(shouldhaveshards を忘れた)




postgres=# (psql)
postgres=# SELECT count(*) FROM pg_dist_shard WHERE logicalrelid=’orders’::regclass;
count
——-
0
(1 row)

私が最初につまずいた問題です。create_distributed_table はエラーなく完了するのにシャードが 0 のままで、データは普通の単一テーブルとして動きます。citus_set_node_property('localhost', 5432, 'shouldhaveshards', true) を実行してから再度試してください。テーブルが作成済みの場合は一度 DROP TABLE orders してやり直す必要があります。

③ERROR: replication_factor (1) exceeds number of worker nodes (0)




postgres=# (psql)
ERROR: replication_factor (1) exceeds number of worker nodes (0)
HINT: Add more worker nodes or try again with a lower replication factor.

citus_set_coordinator_host を実行していない、または実行後に shouldhaveshards を設定していない状態です。STEP 5 の手順を最初からやり直してください。

まとめ

Ubuntu 22.04 に Citus 14.1 をインストールして、PostgreSQL を水平分散させるまでの手順を実測で確認しました。

  • Citus リポジトリを追加し postgresql-16-citus-14.1apt install するだけで入る
  • shared_preload_libraries = 'citus' → 再起動 → CREATE EXTENSION citus が有効化の最短路
  • シングルノードでは shouldhaveshards = true が必須。忘れるとシャードが 0 のまま無言で終わる
  • 実行計画に Custom Scan (Citus Adaptive) と出れば Citus の分散クエリが動いている
  • Ubuntu 22.04 で postgresql-16-citus-14.1 をインストールし、citus_version() で Citus 14.1.0(PostgreSQL 16.14)を実測確認した

本番でスケールアウトするときは、ワーカーノードを追加して citus_add_node('worker-host', 5432) で登録し、rebalance_table_shards('orders') でシャードを再分散できます。まずはシングルノードで DDL と基本的な SQL の動作を確認してから、段階的にノードを増やすのが現実的です。

シャード数の設定(shard_count)は後から変えるのが手間なので、最初から本番を想定した数にしておくことをおすすめします。一般的に 2 × ノード数 以上が目安です。

VPS で Citus を使う場合、ノード間の通信レイテンシが性能に直結します。東京リージョンを選ぶと国内ユーザーへのレイテンシが小さく、ノード間通信も速くなります。

PostgreSQL の基本的なサーバー構築から始めたい場合は も参考にしてください。

コメント

タイトルとURLをコピーしました