mcpField
Y
ysql

ysql

github.com/yugabyteデータ・データベース969 回インストール1 週間前に更新
リポジトリ

ysql について

ysqlは、YugabyteDB YSQLの開発・運用向けベストプラクティススキルです。分散型のPostgreSQL互換データベースを設計するエンジニア向けに、ドライバー接続、トランザクション再試行、シャーディング、インデックス、クエリ最適化、移行評価を扱います。

主な機能

  1. 01複数言語のドライバー接続例
  2. 02トランザクション再試行パターン
  3. 03主キー、シャーディング、コロケーション設計
  4. 04インデックス設計とクエリ診断
  5. 05Voyager移行評価のガイド

活用シーン

  1. 01YSQLの接続と負荷分散方式を選定
  2. 02分散テーブルのキーとシャーディングを設計
  3. 03クエリプランやインデックス効率を調査
  4. 04Voyager移行とスキーマ変換を評価

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/yugabyte/yugabytedb-skills --skill ysql

インストール後、Claude Code で/ysqlを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
bnl-joins.md
query-optimization.md
retry-patterns.md
smart-drivers.md
voyager.md

詳細

YugabyteDB YSQL のベストプラクティス

このスキルには、以下が含まれます。

  • references/smart-drivers.md — 9 種類すべてのスマートドライバーの接続例:Python(psycopg3、psycopg2)、Java(JDBC、R2DBC)、Go、Node.js、C#、Rust、Ruby
  • references/retry-patterns.md — Python と Java でのトランザクション再試行コード
  • references/bnl-joins.md — Batched Nested Loop 結合の設計上の影響、GUC のデフォルト値、診断
  • references/query-optimization.md — timestamp と now() の違いによってインデックスへのプッシュダウンが妨げられる例
  • references/voyager.md — YugabyteDB Voyager による移行評価とスキーマ変換のレビュー

YugabyteDB は分散型の PostgreSQL 互換データベース(ポート 5433 の YSQL)で、ACID 準拠、高可用性、水平スケーラビリティを備え、テーブルとインデックスのハッシュ/範囲シャーディングをサポートしています。設計上の選択では、読み取り効率、書き込みのスケーラビリティ、運用コストのバランスを取る必要があります。

接続 URI の例:postgresql://yugabyte:yugabyte@localhost:5433/yugabyte

重要:接続エンドポイントを 1 つ指定するだけでは、ロードバランシングは保証されません。利用可能な場合はドライバー固有のトポロジー/ロードバランシング機能を使用し、必要に応じてインフラストラクチャのロードバランシング(CSP/Kubernetes/Istio など)と組み合わせてください。

スキーマ設計

SQL の例は、それぞれ独立したスキーマの選択肢を示すものであり、順番に実行する 1 つのマイグレーションではありません。インデックスの例は、名前が示されたテーブルとカラムが存在することを前提としています。ハッシュシャーディングの例では、非コロケーションのテーブルを使用しています。

主キーの戦略

安定していて、適切に分散される場合は、自然主キーを優先してください。サロゲートキーは、レガシーシステムとの連携や相互運用性のために役立つことが多いものの、分散システムで主キー検索に使う形として常に最適とは限りません。

範囲シャーディングされたアクセスパスでは、キーの先頭に単調増加する値(シーケンス、タイムスタンプ、UUIDv7、辞書順に増加するテキスト ID)を使用しないでください。

オプション 1:ハッシュ+範囲の形式を持つ自然キー — ドメインにすでに安定したキーがある場合に最適です。

CREATE TABLE order_lines (
    order_id UUID,
    line_id INT,
    sku TEXT,
    qty INT,
    PRIMARY KEY ((order_id) HASH, line_id ASC)
);

これにより、1 つの注文に属するすべての行がまとまり、異なる注文は均等に分散されます。

オプション 2:UUID のサロゲートキー — 自然キーを利用できない場合に最適です。

CREATE TABLE orders (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    customer_id UUID NOT NULL,
    total DECIMAL(12,2),
    created_at TIMESTAMPTZ DEFAULT now()
);

UUIDv4 形式のランダム性により、書き込みが均等に分散されます。

オプション 3:HASH シャーディングを使用した IDENTITY — 連番 ID が必要な場合に最適です。

CREATE TABLE tickets (
    id BIGINT GENERATED ALWAYS AS IDENTITY,
    title TEXT,
    PRIMARY KEY (id HASH)
);

この注意事項は、テナントのサイズに大きな偏りがある場合に、単一のハッシュ化されたテナントキー(たとえば PRIMARY KEY ((tenant_id) HASH, ...))を使用することに関するものです。大規模テナントのワークロードでは、負荷をより適切に分散する複合ハッシュキー(たとえば PRIMARY KEY ((tenant_id, order_id) HASH, ...))を使用してください。

選択の指針: まず分散された自然キーを検討し、次に UUID を検討します。数値のシーケンス生成 ID が必要な場合は、IDENTITY + HASH を使用してください。シーケンスのキャッシュと並行トランザクションのため、これらの ID は欠番がなくなることも、コミット時刻順になることもありません。

シャーディング:ハッシュと範囲

PostgreSQL との主なスキーマ上の違いは、主キーとインデックスにハッシュシャーディングと範囲シャーディング(HASH/ASC/DESC)のどちらを選ぶかです。

範囲シャーディング(ASC/DESC):

  • 範囲スキャンと ORDER BY ... LIMIT でキーの順序を保持します
  • 単調増加する挿入(NOW()、シーケンス、UUIDv7、増加するテキスト ID)は最新の範囲タブレットに追記され、ホットシャードやタブレットサイズの不均衡を引き起こします
-- Hash PK distributes table writes; plain ASC timestamp index can still hotspot on monotonic inserts
CREATE TABLE sensor_events (
    event_id UUID DEFAULT gen_random_uuid(),
    sensor_id UUID NOT NULL,
    event_ts TIMESTAMPTZ NOT NULL,
    payload JSONB,
    PRIMARY KEY (event_id HASH)
);

CREATE INDEX idx_sensor_events_ts ON sensor_events (event_ts ASC);

重要:テーブルをハッシュシャーディングしても、セカンダリインデックスのホットスポットを自動的に防げるわけではありません。単調増加するインデックスキー(たとえば event_ts ASC/DESC)は、最新のインデックス範囲に書き込みを集中させることがあります。

ハッシュシャーディング(HASH):

  • 異なるハッシュキーをタブレット間で分散します。ただし、1 つのハッシュキーへのトラフィックに偏りがあると、ホットスポットが発生することがあります
  • 同じハッシュ内のポイントクエリや複数行検索に効率的です
  • ハッシュキーの順序は保持されません
  • 先頭のキーが単調増加する場合、セカンダリインデックスでもホットスポットが発生することがあります
  • 書き込みの分散と順序付きの取得の両方が必要な場合は、ハッシュと範囲を組み合わせてください
-- Hash distributes writes; range key preserves per-player order
CREATE TABLE player_transactions (
    player_id UUID,
    created_at TIMESTAMPTZ,
    txn_id UUID DEFAULT gen_random_uuid(),
    payload JSONB,
    PRIMARY KEY ((player_id) HASH, created_at DESC, txn_id ASC)
);
タブレット数の管理

オーバーヘッドを減らすため、タブレットの総数を最小限に抑えます。

  • 平均的なワークロードでは、データベースの初期タブレット数をデフォルトで使用します。SPLIT INTO を省略しても、必ず単一のタブレットが作成されるとは限りません
  • 初期タブレット数によって初期パフォーマンスが大きく変わる、高い取り込み量や高スループットのワークロードに限り、事前分割(SPLIT INTO)を行います
  • YugabyteDB はデータの増加に応じて自動分割します。まずは控えめな数から始め、テレメトリーで必要性が確認できたら増やします
  • 実用上可能な場合は、アクセス頻度の低い小さなテーブルをコロケーションします
コロケーション

完全にコロケーションされたデータベースは、ストレージと書き込みワークロードが単一のタブレットに収まるデータセットに適しています。コロケーションガイドでは、50 GB 未満のデータベースを一般的なユースケースとして挙げていますが、これは容量の保証ではありません。コロケーションされたタブレットは自動分割されないため、大規模なテーブルや書き込み負荷の高いテーブルはコロケーションの対象外にしてください。

CREATE DATABASE myapp WITH colocation = true;
-- Tables share a colocation tablet by default, reducing distributed reads
-- Opt out large/high-throughput tables: WITH (colocation = false)

ホットテーブルを作成するときは、WITH (colocation = false) を設定します。既存のテーブルをコロケーション対象外にするには、置き換え用のテーブルを作成し、計画的な切り替えによってデータを移行する必要があります。ストレージオプションをインプレースで変更することはできません。テーブルを再作成する際は、制約、インデックス、権限、依存関係を維持してください。

インデックス設計

YugabyteDB では、セカンダリインデックスのスキャンで、ベーステーブルの列を取得するために追加のストレージリクエストが必要になる場合があります。これらのリクエストはノード間をまたぐことがあり、複数行の取得をバッチ処理できます。行ごとにリモート RPC が必ず発生するわけではありません。実際のクエリワークロードに基づいてインデックスを設計し、ストレージリクエストを測定してください。

-- PG15-based YSQL: prioritize by total execution time
SELECT queryid, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

-- Then validate plan quality and rows scanned
EXPLAIN (ANALYZE, DIST, COSTS) SELECT ...;  -- check for Seq Scan, Storage Read Requests, Storage Rows Scanned

古い PG11 ベースの YSQL では、代わりに total_time と mean_time が公開されています。監視クエリを調整する前に、インストールされているビューを確認してください(\d pg_stat_statements)。対象リリースについては、クエリ統計のリファレンスを参照してください。

カバリングインデックス — 適切な場合は、クエリで必要な、インデックスからは取得できない列を INCLUDE で追加します。これにより、Index Only Scan でベーステーブルの取得を回避できます。

-- Fetching device_type requires base-table access
CREATE INDEX idx_sessions_account_seen ON sessions (account_id, seen_at DESC);
SELECT account_id, seen_at, device_type
FROM sessions
WHERE account_id = $1
ORDER BY seen_at DESC
LIMIT 50;

-- GOOD for read-heavy query: Index Only Scan candidate
CREATE INDEX idx_sessions_account_seen_cover
ON sessions (account_id, seen_at DESC) INCLUDE (device_type);

トレードオフ:頻繁に更新される列にインデックスを付けると、書き込み増幅が大きくなります。読み取りが多く、更新頻度の低い列にはカバリングインデックスを優先してください。

複合インデックス — 複数列を使うクエリ向けです。列の順序が重要です。等価条件の列を先に、範囲条件の列を最後に配置してください。

-- Better distributed example for YugabyteDB
CREATE INDEX idx_player_txn_player_created
ON player_transactions (player_id HASH, created_at DESC);

-- Uses index:
-- WHERE player_id = $1 AND created_at > now() - interval '7 days'
-- WHERE player_id = $1
-- Does not use index efficiently:
-- WHERE created_at > now() - interval '7 days'  -- missing equality on player_id

-- Covering variant:
CREATE INDEX idx_player_txn_cover
ON player_transactions (player_id HASH, created_at DESC) INCLUDE (payload);

列ではなく、クエリにインデックスを付けてください。実際のクエリ形状で使われない、推測に基づく単一列インデックスは避けてください。

部分インデックス — クエリが実際に必要とする行だけにインデックスを付けます。インデックスが小さいほど、ストレージ使用量が減り、書き込みとスキャンが速くなります。

-- Only active users (soft-delete pattern)
CREATE INDEX idx_users_active_email ON users (email) WHERE deleted_at IS NULL;

-- Only pending orders (completed orders rarely queried by status)
CREATE INDEX idx_orders_pending ON orders (created_at DESC) WHERE status = 'pending';

-- Only non-null values (high null_frac columns)
CREATE INDEX idx_orders_shipped ON orders (shipped_at) WHERE shipped_at IS NOT NULL;

-- Check null fraction to decide: SELECT attname, null_frac FROM pg_stats WHERE tablename = 'orders';

外部キーインデックス — 親テーブルの更新や削除、または子テーブルの検索で必要な場合は、参照側(子)の列にインデックスを付けてください。適切な既存インデックスがない場合、親行の削除を確認またはカスケードする際に、子テーブル全体をスキャンすることがあります。互換性のある既存の複合インデックスが、この目的をすでに満たしている場合もあります。

CREATE INDEX idx_orders_customer ON orders (customer_id);

インデックスのオーバーヘッド — インデックスの維持にはストレージへの書き込みが伴い、RPC が追加で発生する場合もあります。推測でインデックスを追加せず、pg_stat_statements と EXPLAIN (ANALYZE, DIST) を使って、クエリで必要かどうかを確認してください。先頭列が共通していても、2 つのインデックスが冗長とは限りません。一方を削除する前に、キー全体、ハッシュ/範囲の配置、ソート順、述語、含まれる列、一意性や制約への依存関係を比較してください。

インデックスの順序付けとスケーラビリティ

ASC/DESC キーに対する単調増加の挿入をスケールさせながら、順序付きの読み取りを維持するにはどうすればよいでしょうか。 カーディナリティの低いバケットプレフィックスを使用します。

CREATE INDEX idx_events_bucket_ts
ON events ((yb_hash_code(timestamp) % 3) ASC, timestamp ASC)
SPLIT AT VALUES ((1), (2));

バケット設計の推奨事項:

  • ノード数だけでなく、予想される書き込みスループットを基にバケット数を決め、必要に応じて増やしてください
  • 一意インデックス/PK の設計では、yb_hash_code(...) の入力を一意キー列のサブセットにしてください
  • SPLIT AT VALUES は任意ですが、初期分散を予測可能にするため推奨します
  • バケット式はインデックスキーの先頭に置いてください
  • タイムスタンプによるバケット分割では、異なるタイムスタンプ値は分散されますが、同じタイムスタンプを共有する多くの行は同じバケットに集中します
  • 全体を通した順序付き読み取りでは、バケット間のマージが必要です。自動マージスキャンはリリースとプランナーの設定(yb_max_merge_scan_streams、旧称 yb_max_saop_merge_streams、および v2025.2 系列で同時に導入された派生述語の設定 yb_enable_derived_saops と yb_enable_derived_equalities)に依存します。ソートなしのプランだと決めつけず、EXPLAIN でマージストリームのフィールドを確認してください。設定については、バケットベースのインデックスと対象リリースのリリースノートを参照してください
地理的分散配置

テーブルスペース — データをリージョンに固定する: クラスタには、すべての配置ブロックの条件を満たすクラウド/リージョン/ゾーンのラベルを持つ tserver が必要です。

CREATE TABLESPACE us_east_ts WITH (
    replica_placement = '{"num_replicas": 3, "placement_blocks": [
        {"cloud": "aws", "region": "us-east-1", "zone": "us-east-1a", "min_num_replicas": 1},
        {"cloud": "aws", "region": "us-east-1", "zone": "us-east-1b", "min_num_replicas": 1},
        {"cloud": "aws", "region": "us-east-1", "zone": "us-east-1c", "min_num_replicas": 1}
    ]}'
);

CREATE TABLESPACE eu_west_ts WITH (
    replica_placement = '{"num_replicas": 3, "placement_blocks": [
        {"cloud": "aws", "region": "eu-west-1", "zone": "eu-west-1a", "min_num_replicas": 1},
        {"cloud": "aws", "region": "eu-west-1", "zone": "eu-west-1b", "min_num_replicas": 1},
        {"cloud": "aws", "region": "eu-west-1", "zone": "eu-west-1c", "min_num_replicas": 1}
    ]}'
);

行レベルの地理的パーティショニング:

CREATE TABLE orders (
    id UUID DEFAULT gen_random_uuid(),
    region TEXT DEFAULT yb_server_region(),
    customer_id UUID,
    PRIMARY KEY (id, region)
) PARTITION BY LIST (region);

CREATE TABLE orders_us PARTITION OF orders FOR VALUES IN ('us-east-1') TABLESPACE us_east_ts;
CREATE TABLE orders_eu PARTITION OF orders FOR VALUES IN ('eu-west-1') TABLESPACE eu_west_ts;

yb_server_region() は、接続先の tserver のリージョンを使用します。データ配置が顧客のリージョンに応じて決まる場合は、代わりに region を明示的に指定してください。パーティションがないリージョンへの挿入は失敗します。パーティションのプルーニングを可能にするには、検索条件にリージョンを含めてください。また、各パーティションのセカンダリインデックスも、そのリージョンのテーブルスペースに作成してください。リージョン別のベーステーブルだけでは、そのセカンダリインデックスは同じ場所に配置されません。地理的パーティショニングを参照してください。

パーティショニング

ソースシステムでサイズ管理だけを目的にパーティショニングしている場合は、より幅の広いパーティションを検討するか、パーティションを使用しないことを検討してください。YugabyteDB はリレーションを自然にシャーディングします。パーティショニングは、地理的分散配置、または時間ベースのデータライフサイクル(古いパーティションを DELETE ではなくデタッチして削除する)の場合にのみ使用してください。

パーティション数は少なく抑えてください。非常に細かいパーティション(たとえば、数年間保持する日次パーティション)は、不要なタブレットのオーバーヘッドを生みます。

パーティショニングによるワークロードへの価値がわずかな場合は、モデルをよりシンプルに保ってください。

データ型に関するガイダンス
  • JSONB: スキーマが真に動的なシナリオにのみ使用してください。頻繁にアクセスするパターンでは、通常の列のほうが JSONB よりも高いパフォーマンスを発揮します。
行サイズと列サイズに関するガイダンス
  • 行サイズ: 一貫したレイテンシを実現するには、10 MB 未満を推奨します。32 MB は文書化されている最大値であり、レイテンシの目標値ではありません。データモデリングの制限を参照してください。
  • 列サイズ: 列ごとに 2 MB 以下を目標にしてください。

アプリケーションパターン

スマートドライバー(クライアント側のロードバランシング)

アプリケーションから検出されたアドレスに接続できる場合は、スマートドライバーを使用して tserver を検出し、クライアント側で接続を分散します。ロードバランシングを有効にし、ゾーンを考慮したルーティングが必要な場合はトポロジキーを追加します。どちらのパラメーター名もドライバーによって異なるため、使用するドライバーに対応する組み合わせを正確に指定してください。

ドライバーロードバランシングを有効にする設定トポロジキー
psycopg2、pgx、rust-postgres、ruby-pgload_balance=truetopology_keys
psycopg3load_balance_hosts=truetopology_keys
JDBCload-balance=truetopology-keys
R2DBCloadBalanceHosts=truetopologyKeys
node-postgresloadBalance: truetopologyKeys
NpgsqlLoad Balance Hosts=trueTopology Keys

**psycopg3 のトポロジキーを指定すると、アプリケーション接続先は同等の優先度を持つ配置に制限されます。優先度サフィックスはサポートされず、これらの接続ではクラスター全体へのフォールバックも行われません。**ブートストラップおよび検出用の制御接続は、クラスター内の他のノードにも接続できます。ゾーンの可用性が必要な場合は、複数のゾーン(またはリージョンの * ゾーン)を許可してください。許可された tserver のいずれも稼働していない場合、接続は失敗します。クラスター全体でロードバランシングを行う場合は、トポロジキーを省略してください。rust-postgres では、厳密な配置要件がある場合に限り fallback_to_topology_keys_only=true を指定し、必要に応じて複数のゾーンを許可してください。いずれかのポリシーを設定する前に、ドライバーの例と障害に関するガイダンスをお読みください。

**Python ドライバーの選択:**コードベースですでに使用されているドライバーに合わせてください。psycopg3 → psycopg-yugabytedb(インポート名は psycopg のままです)。psycopg2 → psycopg2-yugabytedb。新しいコードで psycopg2 を既定として使用しないでください。現在のドライバーは psycopg3 です。

Python スマートドライバーをインストールする前に、環境を確認してください。psycopg-yugabytedb は、アップストリーム版の psycopg、psycopg-binary、psycopg-c と分けて使用し、このフォークにはシステムの libpq を使用してください。psycopg2 では、アップストリーム版の psycopg2 パッケージを導入せず、YugabyteDB 用のバリアントを 1 つ選択してください。langchain_postgres などの依存関係がアップストリーム版 psycopg3 を必要とする場合は、動作中の環境を維持したうえで選択肢を提示してください。ワークロードを別々の環境またはプロセスに分けるか、アップストリームの動作へ明示的に移行します。パッケージや接続設定を変更する前に、パッケージの選択肢と移行手順をお読みください。

9 種類すべてのスマートドライバー(Python psycopg3/psycopg2、Java JDBC/R2DBC、Go、Node.js、C#、Rust、Ruby)の接続例:references/smart-drivers.md を参照してください。

psycopg3 以外のドライバーでは、トポロジキーに cloud.region.zone[:priority](1=プライマリ、2=フォールバック)を使用します。クラスターで実際に使用されている配置ラベルを指定してください。JDBC、pgx、node-postgres のドキュメントでは、リージョン全体のルーティングに aws.us-east.*:1 のようなゾーンワイルドカードを使用する方法が説明されています。ワイルドカードや優先度の構文を使用する前に、選択したドライバーのリンク先ドキュメントを確認してください。ドライバーリファレンスで説明されているとおり、R2DBC と Npgsql では、選択したパッケージリリースに対応するソースでワイルドカードのサポートが確認できない限り、明示的なゾーンを使用してください。psycopg3 もゾーンワイルドカードをサポートしていますが、優先度サフィックスは使用できません(aws.us-east.*)。クラウドおよびリージョンのワイルドカードは拒否されます。トポロジリファレンスを参照してください。

同時実行制御:競合時に待機

v2024.1以降、YSQLでは競合時に待機がデフォルトです(enable_wait_queues=true)。競合するトランザクションは待機しますが、ブロッカーが互換性のない変更をコミットした後に中止されることはあります。待機キューが有効な場合、明示的に無効化しない限りデッドロック検出も有効です。古いデプロイメントやカスタマイズされたデプロイメントでは、これらのフラグを確認してください。

競合の激しいワークロードでは、wait_queue_poll_interval_msをドキュメントに記載されたデフォルト値付近に保ってください。値を大きくすると、テールレイテンシが悪化する可能性があります。値を小さくする前にベンチマークを実施してください。ポーリング頻度を上げるとオーバーヘッドが増加します。同時実行制御を参照してください。

トランザクションの再試行

40001(シリアライゼーション失敗)と40P01(デッドロック)は、書き込みの同時実行時には依然として通常発生します。ロールバック、指数バックオフ、ジッター、回数制限(通常は3~10回)を含むクライアント側の再試行を必ず実装してください。書き込み処理は冪等になるよう設計してください。

PythonおよびJavaの再試行コード: references/retry-patterns.mdを参照してください

タイムアウト

アプリケーションのレイテンシ予算と、正当なクエリ/トランザクションの実行時間に基づいて、statement_timeoutとidle_in_transaction_session_timeoutを選択してください。以下の30秒/60秒という値は例であり、最小値ではありません。可能であれば接続/セッション単位で適用してください。データベースレベルのデフォルト値は、より多くのワークロードに影響します。

# psycopg3 (psycopg-yugabytedb)
import psycopg

conn = psycopg.connect("...",
    options="-c statement_timeout=30000 -c idle_in_transaction_session_timeout=60000")

# psycopg2 alternative (psycopg2-yugabytedb) - use one driver, not both:
# import psycopg2
# conn = psycopg2.connect("...",
#     options="-c statement_timeout=30000 -c idle_in_transaction_session_timeout=60000")
プリペアドステートメント

クエリ値にはパラメータバインディングを使用してください。再利用可能なサーバー側プランを使うには、ドライバーのプロトコルレベルのプリペアドステートメントを設定してください。パラメーター化だけでは、プランの再利用は保証されません。YSQL Connection Managerは、SQLのPREPARE/EXECUTEに伴うセッション固定を発生させずに、プロトコルレベルのプリペアをサポートします。PgBouncerを使用する場合、ドライバーの自動プリペアを有効にする前に、デプロイ済みバージョン、プーリングモード、設定でプリペアドステートメントがサポートされていることを確認してください。Connection Managerのベストプラクティスを参照してください。

バッチ操作

実用上可能な場合は、バッチ処理/マイクロバッチ処理を使用してください。複数行のINSERTとINSERT ON CONFLICTは、通常、ネットワークの往復回数を減らせるため、単一行の操作よりも高いパフォーマンスを発揮します。

INSERT INTO orders (id, customer_id, total) VALUES
    (gen_random_uuid(), $1, $2),
    (gen_random_uuid(), $3, $4),
    (gen_random_uuid(), $5, $6);
COPYによる一括読み込み
COPY orders FROM '/path/data.csv' WITH (FORMAT csv, HEADER, ROWS_PER_TRANSACTION 20000);
  • ROWS_PER_TRANSACTION(デフォルトは20000)は、COPYを明示的なトランザクションまたは複数コマンドのバッチの外で単独実行する場合に、対象となる通常のテーブルのコミットバッチサイズを制御します。ROWS_PER_TRANSACTION 1000の場合、3500行目でエラーが発生すると、1~3000行目はコミット済みとなり、実行中のバッチはロールバックされます。明示的なトランザクション内、または未対応のテーブル/トリガー構成では、このオプションは警告とともに無視されます。部分コミットを前提とする前に、ドライバーのトランザクションモードとサーバーの警告を確認してください。
  • ファイル名は、接続先のYB-TServer上のものです。クライアントローカルのファイルには、ysqlshのクライアント側\copyまたはドライバーのCOPY APIを使用してください。
  • DISABLE_FK_CHECK — 参照整合性がすでに確認されている場合にのみ、外部キーのチェックをスキップします。
  • SKIP n — すでに読み込み済みの行をスキップして、部分的な失敗後に再開します。
  • コロケートされたテーブルの場合、SET yb_fast_path_for_colocated_copy = on;を設定すると、COPYが明示的なトランザクションの外で実行され、ROWS_PER_TRANSACTIONを省略し、対象にトリガー、ルール、外部キーがない場合に限り、ファストパスが有効になります。アトミックなバッチサイズは、インデックス書き込みも含めてysql_session_max_batch_sizeに従います。COPYのオプションとファストパスの要件を参照してください。
大量の DELETE より TRUNCATE

DELETE はトゥームストーンを書き込み、コンパクションの負荷を増やします。ロック、外部キー、トランザクションの挙動が処理に適合する場合に限り、完全なクリーンアップには TRUNCATE を検討してください。YSQL の TRUNCATE ドキュメントでは、複数ステップのトランザクション内や、読み取り/書き込みと同時に使用しないよう注意しています。これらのセマンティクスが必要な場合は、トランザクション対応の DELETE を使用してください。 時系列データの場合:ALTER TABLE events DETACH PARTITION old_partition; DROP TABLE old_partition;

シーケンスの CACHE
CREATE SEQUENCE order_seq CACHE 100;

ID 列は暗黙的なシーケンスを使用します。最小値が無効化されていない限り、実効割り当てキャッシュは少なくとも ysql_sequence_cache_minval(デフォルトは 100)です。より大きなシーケンス CACHE を指定した場合は、そちらが優先されます。ワークロードに合わせてキャッシュサイズと接続単位/サーバー単位のキャッシュを調整し、ロールバックやキャッシュ値の消失後には欠番が生じる可能性を想定してください。シーケンスのキャッシュを参照してください。

EXPLAIN (ANALYZE, DIST)
EXPLAIN (ANALYZE, DIST, COSTS) SELECT * FROM orders WHERE customer_id = $1;

主なメトリクスは、Storage Read Requests(RPC)、Storage Rows Scanned、およびスキャンの種類です。

返却行数が少ない場合、Index Scan で問題ないことがよくあります。より多くの列を取得する場合は、選択した列をインデックスがカバーしていれば、Index Only Scan によってリソース使用量を抑え、レイテンシを改善できます。

結合戦略:Nested Loop と Batched Nested Loop(BNL)

YugabyteDB は、標準的な PostgreSQL の結合方式(Nested Loop、Hash Join、Merge Join)に加えて、YB 固有の方式である **Batched Nested Loop Join(BNL)**をサポートしています。これは、外側の各行につき 1 回の RPC を行う代わりに、内側のテーブルの検索を = ANY(ARRAY[...]) 呼び出しにまとめて実行します。

設計上の考慮事項、GUC のデフォルト値、およびバッチ処理されるべきプランがそうならない場合の診断方法:references/bnl-joins.mdを参照してください。

長時間実行される読み取りスナップショット

競合を避けながら一貫性のある読み取りを必要とするバッチジョブの場合:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE;
-- long-running queries here
COMMIT;

スナップショットの取得時には、設定された最大クロックスキュー(デフォルトは 500ms)が解消されるまで待機することがあります。これにより、起動時のレイテンシと引き換えに読み取り再試行エラーを回避します。以下のフォロワー読み取りは、古い結果を許容するワークロード向けの別の選択肢です。読み取り再試行エラーの軽減を参照してください。

フォロワー読み取り(低レイテンシの古い読み取り)

読み取り専用クエリで近くのレプリカを使用できるようにします。遅延中または利用できないフォロワーがある場合は、別のレプリカまたはリーダーへのフォールバックが発生することがあります。

SET yb_read_from_followers = true;
SET yb_follower_read_staleness_ms = 30000; -- 30s; check clock-skew constraints before reducing
SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY;
SELECT * FROM analytics WHERE region = 'us-east'; -- eligible for follower reads
  • READ ONLY トランザクション内で実行する必要があります — 読み書きトランザクションでは、フォロワー読み取りは無視されます。
  • リーダーから読み取る場合も古い結果が適用されます — すべての読み取りは yb_follower_read_staleness_ms だけ古くなります。
  • 古さを抑える場合は、デプロイ済みリリースのクロックスキュー制約と、ドキュメントに記載されたハートビートの推奨事項の両方を満たしてください。2025.2 のトランザクションマネージャーでは、yb_follower_read_staleness_ms * 1000 > 2 * max_clock_skew_usec が必要です。最大クロックスキューがデフォルトの 500,000 マイクロ秒の場合は、1000 ms を厳密に超える値を使用してください。
  • 1 秒未満の鮮度が不要なダッシュボード、分析処理、読み取りレプリカに最適です。
  • これらのセッション設定は後続のトランザクションにも影響するため、接続をプールに戻す前にリセットしてください。フォロワー読み取りだけでは、複数の READ COMMITTED 文に対して安定したスナップショットが得られるわけではありません。フォロワー読み取りの挙動と分離レベルとの相互作用を参照してください。
yb_hash_code

大規模な READ/WRITE 操作をチャンクに分割するには、yb_hash_code を使用します。

SELECT * FROM large_table WHERE yb_hash_code(id) BETWEEN 0 AND 5000;
SELECT * FROM large_table WHERE yb_hash_code(id) BETWEEN 5001 AND 10000;

効率的なパーティション境界スキャンのため、すべての HASH キー列をキーの順序どおりに yb_hash_code に渡します。この 2 つの範囲は部分的な例にすぎません。完全なジョブでは、隙間や重複がないように 0 から 65535 までをカバーする必要があります。範囲シャーディングされたテーブルでは、キーの境界でチャンクに分割してください。デプロイ済みリリースの並列クエリのサポートを評価し、EXPLAIN で実際のプランを確認してください。

アドバイザリロック

アドバイザリロックは v2025.1 以降で利用可能で、デフォルトで有効です。デプロイ済みリリースのアドバイザリロック関連フラグを確認してください。PostgreSQL のアドバイザリロック関数を使用します。YugabyteDB 固有の注意事項は次のとおりです。

  • ロックは pg_advisory_locks システムテーブルを介してグローバルに分散されます(共有メモリではありません)。すべてのノードから参照できます。
  • **セッションの固定が必要です。**セッションレベルのロックはバックエンド接続に紐づきます。スマートドライバーやコネクションプーリングを使用する場合は、セッションが同じ接続に留まるようにしてください。
  • トランザクションプーリングを使用する処理では、トランザクションレベルのアドバイザリロックを優先してください。セッションレベルのロックでは、ロック解除までバックエンドを保持する必要があります。明示的ロックを参照してください。

PostgreSQL の移行戦略

次の 4 つのレベルを段階的に適用してください。レベル 1 から始め、ワークロード上の必要性が生じた場合にのみ次のレベルに進んでください。

レベル 1:リフト&シフト(必ず実施)
  • サポートされていない機能を置き換えます(PG 機能対応表を参照)。
  • アクセスパターンに基づいてハッシュシャーディングと範囲シャーディングを選択します。書き込み負荷の高いパスでは、単調増加するキーを先頭に持つ範囲シャーディングを避けてください。
  • 不足しているインデックスを追加します。分散環境では、インデックスの不足によるパフォーマンス低下は単一ノード環境よりも大きくなります。
  • キー、順序、述語、格納列、制約への依存関係を比較したうえで、冗長なインデックスを削除します。
  • クエリの述語がインデックスの述語を含意する場合、ほとんどが NULL である列には部分インデックスを検討してください。
レベル 2:読み取りレイテンシーの最適化
  • 重要なクエリには、カバリングインデックス(キー + INCLUDE)を検討し、インデックスの幅と書き込みコストのバランスを取ってください。
  • コンパクトで安定しており、頻繁な検索に適合する場合は、自然主キーを検討してください。
  • キー列または格納列が変更されると、カバリングインデックスのメンテナンス作業が増えます。読み取りの削減効果と書き込みのオーバーヘッドのバランスを取ってください。
レベル 3:書き込みの線形スケーラビリティ
  • タイムスタンプ列にモジュロバケット化インデックスを適用します(上記の「インデックスの順序付けとスケーラビリティ」を参照)。
  • **ORDER BY LIMIT に関する注意点:**モジュロバケット化インデックスは、全体として順序付けされた結果を生成しません。各バケットの出力をマージしてください。
SELECT * FROM (
    (SELECT * FROM events WHERE (yb_hash_code(timestamp) % 3) = 0 ORDER BY timestamp DESC LIMIT 10)
    UNION ALL
    (SELECT * FROM events WHERE (yb_hash_code(timestamp) % 3) = 1 ORDER BY timestamp DESC LIMIT 10)
    UNION ALL
    (SELECT * FROM events WHERE (yb_hash_code(timestamp) % 3) = 2 ORDER BY timestamp DESC LIMIT 10)
) sub ORDER BY timestamp DESC LIMIT 10;

バケットベースのスキャン最適化はバージョンに依存し、v2025.2 系列でプレビュー/早期アクセス機能として導入されました。v2025.2.3 以降では、代表的なクエリを使ってこれらのセッション設定を評価してください。以前の設定名 yb_max_saop_merge_streams は非推奨であり、yb_max_merge_scan_streams が後継です。有効化する前に、デプロイ済みのパッチリリースとバケットベースのインデックスに関するドキュメントを確認してください。yb_max_merge_scan_streams、yb_enable_derived_saops、yb_enable_derived_equalities は v2025.2 系列以降でのみ定義されています。それ以前のリリースでは、それぞれの SET が unrecognized configuration parameter で失敗するため、設定ブロックは最初に見つかった未定義の設定で中断し、機能を低下させて続行することはありません。ブロックとして実行する前に、デプロイ済みのリリースを確認してください。

ANALYZE events;
SET yb_max_merge_scan_streams = 64;
SET yb_enable_derived_saops = true;
SET yb_enable_derived_equalities = true;
SET yb_enable_cbo = on;

対象となるプランでは、オプティマイザーがバケットストリームをマージするため、手動の UNION ALL は不要です。EXPLAIN で Merge Sort Key、Merge Stream Key、Merge Streams を確認してください。すべてのクエリが対象になると想定しないでください。ワークロードを検証してから、データベース全体のデフォルト値を適用してください。同じタイムスタンプの結果を確定的な順序で返す必要がある場合は、すべての並べ替えに一意のタイブレーカーを追加してください。

レベル 4:エンドツーエンドの最適化
  • インデックスのバランスを調整する際は、pg_stat_statementsでクエリレベルのワークロードを測定し、テーブル統計でテーブルごとのアクティビティを確認します。
  • CTEを使ってクライアントとサーバー間のラウンドトリップを減らし、ロジックをデータベース側に移します(注:CTEは最適化フェンスとして働き、プッシュダウンを妨げることがあります。EXPLAINでテストしてください)。
  • 等価検索以外の検索が必要な場合は、ハッシュシャーディングとレンジシャーディングの両方を使います。

アプリケーション設計

代表的なクエリコスト、ネットワークレイテンシ、読み取りと書き込みの比率を用いて、アプリケーションの並行処理を負荷テストします。スループットが向上しなくなるか、レイテンシが目標を超えるまで、処理中のワークを増やします。特に非同期クライアントや並列クエリを使う場合、スレッド数はデータベースのコア数に対する普遍的な倍率ではありません。データベースのCPU使用率、プールの待ち時間、p99レイテンシをまとめて監視してください。接続とクラスターのサイジングを参照してください。

PG 機能の把握

PG 機能YugabyteDB代わりに使用するもの
SERIAL / BIGSERIAL の主キー、または純粋なレンジシャーディングを行う PRIMARY KEY (timestamp, ...)単調増加する値は最新のレンジタブレットに追加され、書き込みがホットスポット化しますPRIMARY KEY (... HASH)、またはレンジシャーディングされた主キーに単調増加しない自然キーのパターンを使用します
CREATE UNLOGGED TABLE現行のYSQLは分散テーブルでは UNLOGGED を受け入れますが、無視します。古いリリースでは拒否される場合があります通常のレプリケーションされたテーブルを使用します。前述のとおり、クリーンアップには TRUNCATE またはトランザクション内の DELETE を選びます
EXCLUDE USING gist(...)、またはGiST/SP-GiSTインデックスの実行計画YSQLではGiST/SP-GiSTはサポートされていませんサポートされている制約を使って再設計するか、競合する検証や書き込みを直列化します。アプリケーションやトリガーだけで確認してから挿入する方法では、競合状態が発生する可能性があります
BRINのみを使うインデックス戦略BRINはサポートされていませんクエリパターンに基づいてB-tree(および部分インデックス/複合インデックス)を使用します
xmin、xmax、ctid分散YSQLテーブルでは、PostgreSQLのヒープ/MVCCにおけるシステム列の動作はサポートされていません楽観的ロックには明示的な version INT 列を使用します
MERGE INTO文書化されているYSQLの互換性制限ではサポートされていませんアップサートには INSERT ... ON CONFLICT ... DO UPDATE SET を使用し、その他のMERGE分岐には明示的なトランザクションロジックを使用します
PREPARE TRANSACTION実装されていませんSagaパターンまたはアウトボックスパターンを使用します

version INT による楽観的ロックはYugabyteDBで有効です。期待するバージョンを比較して同じ書き込みステートメント内でインクリメントし、その後、1行が更新されたことを確認します。ドライバーのプレースホルダー構文(JDBCでは ?、psycopgでは %s)を使って期待値をバインドします。

対象リリースについて、移行時の互換性制限を確認してください。古いバージョンに関する項目もあり、「Fixed In」リリースが記載されています。

緯度/経度の検索パターンでは、広範な事前フィルタリングや範囲スキャンにSQLを使い、必要に応じて中間層で正確な地理的距離を確認します。

本番環境のチェックリスト

  • DDL の安全性: スキーマ変更には単一の接続を使用し、カタログが伝播するまでの時間を確保してください。お使いのバージョン/設定で明示的に有効化されていない限り、トランザクショナル DDL に依存しないでください。
  • テーブルレベルのロック: マイグレーションや運用のフローで必要な場合は、明示的なテーブルレベルのロックパターンを有効にするか、使用してください。
  • DDL の実行モード: お使いの YugabyteDB のバージョンでサポートされている場合は、より安全なオンラインスキーマ変更のため、並行 DDL およびトランザクショナル DDL の機能を評価してください。
  • TLS: sslmode=verify-full と、信頼できる CA を指定する sslrootcert を使用してください。クライアント証明書認証が必要な場合は、sslcert/sslkey を追加してください。
  • オブザーバビリティ: 再試行回数/遅延/SQLSTATE を記録してください。既知の一時的な障害(40001、40P01)は再試行してください。構文/アクセス規則(42xxx)および整合性(23xxx)のエラーは、通常、むやみに再試行するのではなく操作を修正する必要があります。タブレットリーダーの分散状況を監視してください。
  • 楽観的ロック: システム列(xmin/ctid)ではなく、version INT 列を使用してください。
  • コネクションプール: Yugabyte のサイジングガイダンスでは、vCPU あたり 15 のサーバー接続を基準となる最大値として示しています。これはスループットの目標値ではありません。レイテンシに敏感なワークロードでは、より低い同時実行数で負荷テストを行い、メモリも考慮してください。サーバー側のプーラーを使用すると、より少ないバックエンドで多数のクライアント接続を多重化できます。
  • 接続の再利用: ノードを追加しても、既存の接続は移動しません。新しい接続から新たに検出されたノードを使用できるよう、プールの接続有効期間/アイドル接続の再利用設定(たとえば、HikariCP の maxLifetime および idleTimeout)を構成してください。
  • YSQL Connection Manager: 組み込みのサーバー側コネクションプーラーです。構成済みのリスナーポートとクライアント上限を確認してください。セッション機能によってプーリングのコストは異なります。TEMP テーブルと SQL PREPARE は接続を固定化しますが、プロトコルの準備ではこの固定化の原因を回避できます。セットアップと制限事項を確認し、トポロジーを認識した接続ルーティングを行うスマートドライバーと組み合わせてください。
  • DDL マイグレーション: マイグレーションツール(Flyway、Active Record)には単一のデータベース接続を使用してください。分散キャッシュ間での DDL の伝播により、並行接続で制約違反が発生する場合があります。
  • 拡張機能: デプロイ済みリリースの同梱拡張機能とモジュールおよびそのセットアップ要件を確認してください。例として、pg_stat_statements、pgcrypto、pgvector(SQL 名は vector)、pg_cron、pg_partman、postgres_fdw、pgaudit、pg_trgm、pg_hint_plan、uuid-ossp、hstore があります。SQL 拡張機能では通常、CREATE EXTENSION が必要です。また、プリロード/設定も必要なものがあります。auto_explain はロード可能なモジュールであり、CREATE EXTENSION の対象ではありません。

クエリの最適化

タイムゾーンなしの timestamp と now(): now() は timestamptz です。これを timestamp 列と比較すると暗黙の変換が発生し、インデックス条件のプッシュダウンが妨げられます(Rows Removed by Index Recheck / Storage Index Rows Scanned が返された行数を大幅に上回る形で確認できます)。列を timestamptz として定義する(推奨)か、述語内でキャストしてください(now()::timestamp - interval …)。実行可能な EXPLAIN (ANALYZE, DIST) の例:references/query-optimization.md。

コメント