Postgres のCitus 拡張機能の最新リリース Citus 9.4 について
※本ブログは、‘’What’s new in the Citus 9.4 extension to Postgres (citusdata.com)‘’の抄訳です。
Postgres のCitus 拡張機能の最新リリースはCitus 9.4です。馴染みがない方に簡単に説明しておくと、Citus は Postgres を分散データベースに変換し、データと SQL クエリを複数のノードに分散させます。この投稿は実質的には Citus 9.4 のリリースノートです。
Citus を使い始める準備ができているなら、9.4 用の Citus オープンソースパッケージを簡単にダウンロードできます。
詳細を知るには docs.citusdata.com を確認するのが常にお勧めの方法です。Citus のドキュメントには、正確なチュートリアル、すべての Citus 機能の詳細、重要な概念の説明(分散列の選択など)、単一のサーバーでCitusを手元に構築する方法、複数のサーバーにCitusをインストールする方法、リアルタイム分析ダッシュボードを構築する方法、マルチテナントデータベースの構築方法などがあります。
より高度なリソースとして、Citus のオープンソースレポジトリーで実際のソースコードを見ることができるので、私たちが何をしているのか、そしてPostgres の Citus 拡張機能の将来のリリースで何が起こるかを見ることができます。
Citus 9.4の新機能
Citus 9.4 のオープンソースのパッケージとドキュメントはすでに公開されているので、Citus 9.4 で注目すべき新機能全てを見るには良いタイミングだと思います。他の最近のリリースについて知りたい場合は、Citus 9.3のリリースノートや、HTAPパフォーマンスの改良全てについてはCitus 9.2のリリースノート を覗いてみてください。
Citus 9.4 の新機能を深掘りする前に、概要を以下に示します:
- EXPLAIN ANALYZEの改良
- INSERT INTO ローカルテーブル SELECT .. FROM 分散テーブル
- ローカル テーブルと CTE 間の結合
- COPY のコネクション管理
- t-digest によるパーセンタイルの計算のスケール
- ルータ クエリでの CTE の落とし込みの修正
- バイナリ プロトコルの追加サポート
- Citus のダウングレード スクリプト
EXPLAIN ANALYZE のパフォーマンスの理解
Postgresの EXPLAIN ANALYZE コマンドは、クエリのパフォーマンスを理解するために不可欠です。Citus 9.4 では、EXPLAIN ANALYZE が Citus でどのように動作するかを改善し、分散した Citus クエリに関する詳細な情報を提供します。
Citus の内部を覗くと(Citus はオープンソースです)、Citusの EXPLAIN ANALYZE コマンドは、分散した Citus クエリを 2 回実行することがわかります。一度、Citus のコーディネーター ノード上で EXPLAIN のプランを構築するためにワーカー ノードから結果を得て、そして再び Citus のワーカーノードから EXPLAIN のプランを取得します。
この 2 段階のアプローチでは、EXPLAIN が常にキャッシュにヒットするため、頻繁にキャッシュ ミスが発生するワークロードでは誤解を招く結果が生じる可能性があります。
Citus 9.4では、2つのステップを1つに組み合わせて、より正確なEXPLAIN ANALYZE の結果が得られます。さらに、分散した Citus のクエリのすべてのタスクを説明する際に、クラスタ全体で同時に EXPLAIN プランを作成し、EXPLAIN ANALYZE の結果をはるかに高速に実行できるようになりました。この新しいアプローチでは、INSERTやその他の DML コマンドに対するEXPLAIN ANALYZE も有効になります。
Citus に関連する以下の情報も追加しました:
- EXPLAINによる、ネットワーク経由で転送されたバイト数の表示
- EXPLAINによる、ワーカー ノードとの間で送受信される Postgres クエリの表示が可能に
Citus 9.4 の EXPLAIN ANALYZE の機能強化により、Citus データベース クラスタで Postgres クエリのパフォーマンスを理解し、調整することが非常に簡単になりました。例:大きなテーブルでの平均の取得。Citus は、各シャードから合計とカウントのみを取得するため、ネットワークで 44 バイトしか転送しません:
EXPLAIN (ANALYZE, VERBOSE) SELECT avg(rating) FROM item_ratings;
QUERY PLAN
——————————————————————————————————————————————————————–
Aggregate (cost=500.00..500.02 rows=1 width=8) (actual time=95.942..95.942 rows=1 loops=1)
Output: (sum(remote_scan.avg) / (pg_catalog.sum(remote_scan.avg_1))::double precision)
-> Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=100000 width=16) (actual time=95.917..95.918 rows=2 loops=1)
Output: remote_scan.avg, remote_scan.avg_1
Task Count: 2
Tuple data received from nodes: 44 bytes
Tasks Shown: All
-> Task
Query: SELECT sum(rating) AS avg, count(rating) AS avg FROM item_ratings_102077 item_ratings WHERE true
Tuple data received from node: 22 bytes
Node: host=localhost port=9700 dbname=postgres
-> Aggregate (cost=10872.68..10872.69 rows=1 width=16) (actual time=92.390..92.391 rows=1 loops=1)
Output: sum(rating), count(rating)
-> Seq Scan on public.item_ratings_102077 item_ratings (cost=0.00..8141.12 rows=546312 width=8) (actual time=0.120..53.408 rows=495406 loops=1)
Output: user_id, rating
Planning Time: 0.051 ms
Execution Time: 92.424 ms
-> Task
Query: SELECT sum(rating) AS avg, count(rating) AS avg FROM item_ratings_102078 item_ratings WHERE true
Tuple data received from node: 22 bytes
Node: host=localhost port=9701 dbname=postgres
-> Aggregate (cost=11075.68..11075.69 rows=1 width=16) (actual time=93.468..93.468 rows=1 loops=1)
Output: sum(rating), count(rating)
-> Seq Scan on public.item_ratings_102078 item_ratings (cost=0.00..8293.12 rows=556512 width=8) (actual time=0.053..53.639 rows=504594 loops=1)
Output: user_id, rating
Planning Time: 0.090 ms
Execution Time: 93.500 ms
Planning Time: 0.370 ms
Execution Time: 96.191 ms
(29 rows)
アプリが Citus のローカルおよび分散テーブルの両方を活用できることを確認する
INSERT INTO ローカルテーブル SELECT .. FROM 分散テーブル:
クエリの結果をテーブルに INSERT または UPSERT することは、PostgreSQL と Citus での強力なデータ変換メカニズムです。Citus 9.4 以降では、分散テーブルに対する SQL クエリの結果をローカル テーブルに INSERT または UPSERT できるようになりました。これにより有効になる重要な利用法は、レイテンシーを小さくするためにコーディネーターに配置される小さな集計テーブルを保持することですが、他のワークフローも簡素化します。たとえば、分散テーブルに対するクエリの結果を一時テーブルに直接書き込むことができるようになりました。この機能により、Citus は任意のテーブルの組み合わせに対して INSERT..SELECT を利用出来るようになりました。
ローカル テーブルと CTE 間の結合:
Citus コーディネーター上のローカル テーブルと、分散テーブルにクエリするCommon Table Expressions (CTE) の間の JOIN がサポートされるようになりました。これにより、分散テーブル、参照テーブル、ローカル テーブルを混在させるハイブリッド データ モデルを簡単に使用できます。
COPY のコネクションを管理して Postgres のコネクションを使い果たさないようにする
多数のCOPY コマンドを同時に実行した場合、Citus は多数の Postgres のコネクションでワーカー ノードをあふれさせてしまうことがあります。
Citus 9.4 以降では、citus.max_adaptive_executor_pool_size 設定により、個々の COPY コマンドによって作成される Postgres のコネクション数を減らすことができるようになりました。次のリリース (Citus 9.5) では、COPY の接続管理を完全に自動で行います。
t-digest によるパーセンタイルの計算のスケール
t-digest拡張機能から Postgres への集計機能は、Citus で分散することが可能になり、Citus 上で非常に高速で並列なパーセンタイル計算を可能にし、Citus オープンソースユーザーの一部にとって重要なニーズを満たすことができます。オープンソースのt-digest拡張機能を作成し Citus で t-digest を利用可能にする際に大きな助けになったPostgres コミッターの Tomas Vondra に感謝します。オープンソースの t-digest 拡張機能は Hyperscale (Citus) でもサポートされていますが、Hyperscale (Citus) 拡張機能サポートのドキュメントページでも、すぐにt-digestの説明が追加更新される予定です。
パーセンタイルの近似は有用でしょうか? もちろんです。私のチームメイトの一人、Nils Dijk は、あるお客様が t-digest と Hyperscale (Citus) でPostgres のパーセンタイル計算を45倍改善した方法についてブログ記事を書いています。そして、Stackify チームの Matt Watson は、Postgres、t-digest、および Citus を使用して大規模な SQL パーセンタイル集計を作成する方法について書いたところです。また、Microsoft Azure でCitus を使用してペタバイト規模の分析ダッシュボードを管理する Windows データおよびインテリジェンス チームも、Citus で t-digest を使用しています。
パフォーマンスの改善
ルータ クエリでの CTE の落とし込みの修正:
CTE の変更はテーブルの変更に利用することが可能で、その後に変更された行を利用して追加のクエリを実行できます。たとえば、完了した TODO 項目を削除する一方で、カウンタを減らすこともできます:
DELETE FROM todo WHERE user_id = 423 AND isdone RETURNING *
)
UPDATE users
SET remaining_todos = remainining_todos – (SELECT count(*) FROM deleted_todo)
WHERE user_id = 423;
CTE の変更を伴うクエリに、分散列 (たとえば、ユーザー ID やtenant_id) にフィルターが設定されている場合、1 回のネットワーク上のやりとりでワーカーに完全に委任できるので、これらのクエリはより小さなレイテンシーと高いスループットで実行できることになります。
バイナリプロトコルの追加サポート:
Citus 9.4 では、ワーカーとコーディネーター間の通信に、PostgreSQL のバイナリプロトコルを有効にできます。
通常、値はテキストとして経路上を送信され、一部のデータに対しては非常に効率的です。9 という値の bigint 型はテキストで 1 バイトしか必要としませんが、バイナリでは 8 バイトを必要とすることを考慮してください。一方、テキスト形式がバイナリ形式よりも大きいタイムスタンプなどのデータ型もあります。たとえば、PostgreSQL はタイムスタンプを格納するために 8 バイトを使用しますが、テキスト形式 (例: 2020-09-04 09:58:36.788916+02) は 29 バイトです。クエリが数百万行を返す場合、多くの余分なネットワーク トラフィックが発生する可能性があります。
citus.enable_binary_protocolを有効にすると、バイナリプロトコルを使用してクエリを実行できるため、ネットワークトラフィックを削減し、バイナリ表現がテキスト表現(タイムスタンプ、浮動小数点型、t-digestなど)よりもはるかにコンパクトなデータタイプになるのでクエリを高速化できます。バイナリ プロトコルは、場合によってはネットワーク トラフィックを増やす可能性があるため (たとえば、小さな整数)、デフォルトで無効になっていますが、特にクエリが多くのタイムスタンプ、浮動小数点型、またはテキスト表現が非常に長い他のデータ型を返す場合は、パフォーマンスを向上させます。
分散型 Citus クラスターを操作するためのその他の制御
Citus のダウングレード スクリプト:
以前は、Citus のアップグレード スクリプトによってスキーマが変更され、元に戻すことができないため、Citus 拡張は新しいバージョンにアップグレードすることしかできませんでした。Citus 9.4の時点で、我々は各アップグレードスクリプトのダウングレード スクリプトを用意することを約束しました。つまり、アップグレードを行い、何らかの理由で元に戻す必要がある場合は、古いバージョンの Citus をインストールし、ALTER EXTENSION を実行してスキーマの変更を元に戻すことができます。
パフォーマンスがすべてです
Citus ユーザは、最も困難なデータ問題のいくつかを抱えています。Citusユーザは多くのデータを持っているだけではありません。Citus ユーザは通常、少ないデータに対するクエリと同様に、大量のデータに対する Postgres クエリを可能な限り高速にする必要があり、急速に成長する中においてもアプリケーションがスムーズに動作し続けることを担保する目的を持っています。
このレベルのパフォーマンスを実現するには、ハードウェアを最大限に活用する必要があるため、9.4 リリースには優れたパフォーマンスの洞察、パフォーマンスの向上、新しいパフォーマンス チューニングの可能性が満載されています。
Citus Slack で新しい 9.4 リリースについてご存知の通り、バグやパフォーマンスの問題が見つかった場合は、GitHub で報告してください。あなたのフィードバックは、Citusをさらに良くするのに役立ちます。
読んでみて楽しめたでしょうか?
チームからの投稿をもっと読みたければ、毎月のニュースレターに登録すれば、最新のコンテンツが受信トレイに直接配信されます。