mcpField
B
benchmarking-transaction-patterns

benchmarking-transaction-patterns

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

benchmarking-transaction-patterns について

CockroachDB の開発者向けに、明示的な複数ステートメントのトランザクションと単一ステートメントの CTE を比較するベンチマークスキルです。競合時の公平な測定、リトライ、レイテンシ、スループットの評価を扱います。

主な機能

  1. 01独立したベンチマーク用データベースの準備
  2. 02明示的トランザクションと CTE の測定
  3. 03公平な比較のためのデータリセット
  4. 04スループット、リトライ、レイテンシの比較
  5. 05ベンチマークの整合性確認

活用シーン

  1. 01競合時のトランザクション設計の比較
  2. 02リトライとテールレイテンシの評価
  3. 03本番変更前の測定結果の確認

インストール

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

$npx skills add https://github.com/cockroachlabs/cockroachdb-skills --skill benchmarking-transaction-patterns

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

詳細

トランザクションパターンのベンチマーク

CockroachDB における同一のトランザクション型ビジネスワークフローの 2 つの構成、すなわち明示的な複数ステートメントトランザクションと単一ステートメントの CTE トランザクションについて、ベンチマーク、説明、比較を行うためのガイドです。競合発生時のパフォーマンス、公平なテスト方法、結果の解釈に焦点を当てています。

設計スキルの補足: 一般的なトランザクション設計の原則については、designing-application-transactions を参照してください。SQL 構文とクエリパターンについては、cockroachdb-sql を参照してください。

基本概念

競合が発生している状況では、トランザクションの構成自体が主要なパフォーマンス調整要素となります。明示的モデル(複数ステートメントの BEGIN/COMMIT)では、ラウンドトリップ中もトランザクションが開いたままになるため、競合が発生する時間帯が広がります。CTE モデル(単一ステートメント)では、同じロジックを 1 つのアトミックなステートメントにまとめることで、トランザクションの所要時間とリトライを削減します。

明示的トランザクションモデル
BEGIN;
SELECT balance FROM accounts WHERE id = $1;
-- Application decides whether transfer is allowed
UPDATE accounts SET balance = balance - $2 WHERE id = $1;
UPDATE accounts SET balance = balance + $2 WHERE id = $3;
INSERT INTO transfers (from_acct, to_acct, amount, created_at)
VALUES ($1, $3, $2, now());
COMMIT;
CTE トランザクションモデル

CockroachDB では、デフォルトで 1 つのステートメント内で同じテーブルを複数回変更することが拒否されるため(sql.multiple_modifications_of_table.enabled)、引き落としと入金は CASE を使った 1 つの UPDATE にまとめます。

WITH funded AS (
  SELECT 1 FROM accounts WHERE id = $1 AND balance >= $2
), upd AS (
  UPDATE accounts
  SET balance = CASE WHEN id = $1 THEN balance - $2 ELSE balance + $2 END
  WHERE id IN ($1, $3) AND EXISTS (SELECT 1 FROM funded)
  RETURNING id
), ins AS (
  INSERT INTO transfers (from_acct, to_acct, amount, created_at)
  SELECT $1, $3, $2, now() WHERE (SELECT count(*) FROM upd) = 2
  RETURNING id
)
SELECT id FROM ins;

手順

1. ベンチマーク環境を準備する

専用のテストデータベースとスキーマを用意します。ベンチマークのワークロードを他のトラフィックと混在させないでください。

CREATE DATABASE IF NOT EXISTS bankbench;
USE bankbench;

CREATE TABLE accounts (
  id INT PRIMARY KEY,
  balance DECIMAL(18,2) NOT NULL DEFAULT 0
);

CREATE TABLE transfers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  from_acct INT NOT NULL,
  to_acct INT NOT NULL,
  amount DECIMAL(18,2) NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
2. テストデータを投入する

効率よくデータを投入するには、複数行の UPSERT を使用します。単一行の挿入では、セットアップコストが実際とは異なるものになります。

INSERT INTO accounts (id, balance)
SELECT generate_series(1, 10000), 1000.00
ON CONFLICT (id) DO UPDATE SET balance = 1000.00;
3. 明示的トランザクションのベンチマークを実行する

実際の状況に即した同時実行数で実行します。PostgreSQL 互換の pgbench を使用する例を示します。

# Create a pgbench script file: explicit_transfer.sql
# \set from_id random(1, 10000)
# \set to_id random(1, 10000)
# \set amount 10.00
# BEGIN;
# SELECT balance FROM accounts WHERE id = :from_id;
# UPDATE accounts SET balance = balance - :amount WHERE id = :from_id;
# UPDATE accounts SET balance = balance + :amount WHERE id = :to_id;
# INSERT INTO transfers (from_acct, to_acct, amount, created_at) VALUES (:from_id, :to_id, :amount, now());
# COMMIT;

pgbench -n -c 64 -j 8 -T 120 -f explicit_transfer.sql \
  "postgresql://root@localhost:26257/bankbench?sslmode=disable"

スループット(tps)、リトライ数、p50/p95/p99 レイテンシ、最大レイテンシ、失敗数を記録します。

4. 公平に比較するため、実行ごとにリセットする

公平なベンチマークを行うため、明示的モデルと CTE モデルの実行の間でアカウント残高をリセットし、テーブルサイズ、インデックスサイズ、アカウントの状態を同程度に保ちます。

UPDATE accounts SET balance = 1000.00;
5. CTE トランザクションのベンチマークを実行する

明示的モデルの実行と同じ同時実行数、実行時間、パラメーターを使用して実行します。

# Create a pgbench script file: cte_transfer.sql containing the CTE query above
pgbench -n -c 64 -j 8 -T 120 -f cte_transfer.sql \
  "postgresql://root@localhost:26257/bankbench?sslmode=disable"
6. 結果を比較する

以下の指標を必ず並べて比較します。

指標確認する内容
スループット(txn/s)高いほど良好です。CTE は通常、競合時にも高いスループットを維持します
リトライ総数CTE ではほぼゼロまで減ることがよくあります
p50 レイテンシトランザクション時間の中央値
p95 レイテンシ中程度の競合時のテールレイテンシ
p99 レイテンシ最悪時のテールレイテンシ。明示的モデルでは急増することがよくあります
最大レイテンシ外れ値の挙動
失敗数リトライ不可のエラー
7. ベンチマークの信頼性を検証する

結果を解釈する前に、ベンチマークが問題なく実行されたことを確認します。

-- Confirm expected transfer volume
SELECT COUNT(*) AS total_transfers FROM transfers;
# Check node liveness and start times (no node restarts mid-benchmark)
cockroach node status --certs-dir=<certs-dir>     # or --insecure for an insecure cluster

ベンチマークの参考結果

報告されている、競合の激しい状況での 2 つのモデルの比較結果は次のとおりです。

指標明示的モデルCTE変化率
スループット591.1 txn/s1,035.1 txn/s+75.1%
実行時間216.5s123.7s-42.9%
平均レイテンシ202.2 ms111.3 ms-45.0%
リトライ総数2,270,9770-100%

実行規模をさらに大きくした場合も同じ傾向が保たれました。明示的モデルではリトライが増え続け、ときおり失敗も発生した一方、CTE モデルではリトライも失敗もゼロのままでした。

影響の概要
項目明示的な複数ステートメント単一ステートメントの CTE
往復回数クライアントとサーバー間で複数回やり取り単一のリクエスト
トランザクションの存続期間長い短い
クライアント側の再試行の複雑さ高い低い
アトミックな不変条件の適用ステートメント/アプリケーションロジックに分散SQL 内に収まる
予想スループット競合時は低い競合時は高い
クライアントから見える再試行発生しやすい多くの場合減少

判断の指針

明示的なパターンを選ぶ場合
  • ビジネスワークフローを単一の SQL ステートメントで適切に表現できない場合
  • 最大スループットより、読みやすさや段階的なビジネスロジックが重要な場合
  • 競合レベルが低く、再試行の増幅が主要なコスト要因ではない場合
CTE パターンを選ぶ場合
  • ワークフローで競合が多い場合
  • 操作が本質的にアトミックな場合
  • アプリケーションが現在、複数のステートメントにまたがる読み取り・判断・書き込みを実行している場合
  • 主な目的が、スループットの向上、再試行の削減、p95/p99 レイテンシーの安定化である場合

公平なベンチマークのルール

  1. 残高、テーブルサイズ、インデックスサイズを一定に保ち、公平に比較できるよう、実行の間にデータをリセットします
  2. リセットしない実行は、同条件での比較ベンチマークではなく、デモとして扱います
  3. 明確に比較できるよう、一度に 1 つの業務単位を処理する場合は --batch-size=1 を使います
  4. 適切なメトリクスを比較します — スループット、再試行回数、p50、p95、p99、最大レイテンシー、失敗数を必ず含めます
  5. データ投入には複数行の UPSERT を使います — 1 行ずつ投入するとセットアップコストが歪みます

よくある誤解

  • 「CTE は常に優れている」 — ワークフローが競合の影響を受けやすく、単一のアトミックなステートメントで表現できる場合に限ります。
  • 「SQL Activity に待機が表示されるなら、CTE は失敗した」 — CTE は競合を減らしますが、なくすわけではありません。スループット、テールレイテンシー、再試行の傾向を比較してください。
  • 「単一ステートメントなら競合しない」 — CTE は競合が起きる時間帯を狭めますが、競合をなくすわけではありません。

安全上の考慮事項

  • ベンチマークは本番環境ではなく、専用のテストクラスターで実行してください
  • 公平に比較できるよう、実行の間にデータをリセットしてください
  • ベンチマークの実行中はクラスターの健全性を監視してください
  • 現実的でありながら、破壊的ではない同時実行レベルを使用してください
  • 本番環境を変更する前に、ベンチマーク結果が実際のワークロードに当てはまることを検証してください

参考資料

コメント