KoderKoder.ai
料金エンタープライズ教育投資家向け
ログインはじめる

プロダクト

料金エンタープライズ投資家向け

リソース

お問い合わせサポート教育ブログ

リーガル

プライバシーポリシー利用規約セキュリティ利用ポリシー不正利用を報告

ソーシャル

LinkedInTwitter
Koder.ai
言語

© 2026 Koder.ai. All rights reserved.

ホーム›ブログ›Go + Postgres パフォーマンスチューニング:実践的な API プレイブック
2025年12月14日·1 分

Go + Postgres パフォーマンスチューニング:実践的な API プレイブック

AI生成API向けのGo + Postgresパフォーマンスチューニングプレイブック:接続プール、クエリプラン確認、賢いインデックス設計、安全なページング、JSONの高速整形。

Go + Postgres パフォーマンスチューニング:実践的な API プレイブック

Go API が Postgres 上で「遅い」と感じるときの様子

AI 生成の API は初期テストでは速く感じることが多いです。エンドポイントを数回叩き、データセットは小さく、リクエストは一度にひとつだけ来る。ところが本番トラフィックが来ると、エンドポイントが混ざり合い、バーストが発生し、キャッシュが冷え、想定より多くの行が入っていることに気づきます。同じコードが「たまに」遅く感じられるようになりますが、実は何かが壊れているわけではありません。

遅さは通常、いくつかの現象で現れます:レイテンシのスパイク(大多数は問題ないが一部が5x〜50x長くなる)、タイムアウト(小さな割合が失敗する)、あるいは CPU の高負荷(クエリによる Postgres の CPU、または JSON、goroutine、ログ、リトライでの Go 側の CPU)。

よくあるシナリオは、柔軟な検索フィルタを持つ一覧エンドポイントで大きな JSON を返すケースです。テスト DB では数千行をスキャンして素早く終わるかもしれませんが、本番では数百万行をスキャンしてソートし、最後に LIMIT を適用していることがあります。API は動作しているものの p95 レイテンシが爆上がりし、バースト時にいくつかのリクエストがタイムアウトします。

DB の遅さとアプリの遅さを分けて考えるとシンプルになります。

  • データベースが遅ければ、Go ハンドラは大半の時間をクエリの完了待ちに使います。多くのリクエストが "in flight" に詰まり、Go 側の CPU は普通に見えることがあります。
  • アプリが遅ければ、クエリは速やかに終わるが、クエリ後に大きなオブジェクトを組み立てたり、JSON をマーシャリングしたり、行ごとの追加クエリを実行したり、リクエストごとにやりすぎている時間が発生します。Go の CPU とメモリが上がり、レスポンスサイズに応じてレイテンシが増加します。

"ローンチ前に十分" であることは完璧を意味しません。多くの CRUD エンドポイントでは、安定した p95(平均ではない)、バースト時に予測可能であること、想定ピークでタイムアウトが発生しないことを目指してください。目的は明快です:データとトラフィックが増えても驚きの遅延が起きないこと、そして何かが逸脱したときに明確なシグナルがあることです。

まず基準を作る:重要な数値

何かをチューニングする前に、API の「良い」を決めてください。ベースラインがないと、設定をいじっても改善したのか単にボトルネックを別の場所に移しただけなのか分からなくなります。

通常、次の3つの数字で多くが分かります:

  • p95 リクエストレイテンシ(平均ではなく)
  • エラーレート(HTTP 5xx、タイムアウト、キャンセル)
  • リクエストあたりの DB 時間(各リクエストが Postgres を待っている時間)

p95 は「悪い日の」指標です。p95 が高くて平均が問題ない場合、少数のリクエストがやりすぎているか、ロックで詰まっているか、あるいは遅いプランを引いている可能性があります。

スロークエリを早めに可視化してください。Postgres ではプレローンチのテスト時に低めの閾値(例:100–200 ms)でスロークエリログを有効にし、フルステートメントをログに残すと SQL クライアントで再実行して調査しやすくなります。これは一時的にしておきましょう。本番で全てのスロークエリをログするとノイズが急増します。

次に、実際のリクエストに近いものをテストしてください。単一の "hello world" ルートだけでは不十分です。小さなセットで十分ですが、それがユーザーの実際の使い方に合っていることが重要です:フィルタとソートを持つ一覧、いくつかの JOIN を含む詳細ページ、検証を行う作成/更新、部分一致を使う検索スタイルなど。

もし仕様からエンドポイントを生成しているなら(たとえば Koder.ai のようなツールで)、同じ少数のリクエストを一貫した入力で繰り返し実行してください。これによりインデックスやページング、クエリ書き換えの効果を測りやすくなります。

最後に、口に出して言える目標を決めてください。例:「50 同時ユーザーで大多数のリクエストは p95 200 ms 未満、エラーは 0.5% 未満」。具体的な数値はプロダクト次第ですが、明確な目標があればエンドレスな微調整を防げます。

Postgres を安定させる接続プーリング

接続プールは開かれた DB 接続の数を制限し、それらを再利用します。プールがないと各リクエストで新しい接続を開き、Postgres はセッション管理に時間とメモリを使ってしまいます。

目的は Postgres を有用な仕事で忙しくさせ、過剰な接続間のコンテキストスイッチを避けることです。これは特にチャットっぽく多くの短いリクエストを発する AI 生成 API で最初の大きな改善になることが多いです。

シンプルな出発設定

Go では通常、最大オープン接続数、最大アイドル接続数、接続寿命を調整します。小さな API の安全な出発点は CPU コア数の小さな倍数(多くは合計で5〜20接続)、似た数をアイドルに保ち、30〜60分ごとに接続を再利用する、などです。

複数の API インスタンスを動かしている場合、プールは乗算されることを忘れないでください。たとえば 20 接続のプールが 10 インスタンスなら Postgres には 200 接続が来る計算になり、思わぬ接続上限にぶつかります。

プールが問題かどうかの見分け方

プール問題は遅い SQL とは異なる振る舞いをします。

  • プールが小さすぎると、リクエストは Postgres に到達する前に待たされます。レイテンシがスパイクし、DB の CPU やクエリ時間は一見正常に見えることがあります。
  • プールが大きすぎると、Postgres が過負荷になり、アクティブセッションが多くなり、メモリ圧力やエンドポイント間のレイテンシのばらつきが増えます。

素早く切り分ける方法は、DB 呼び出しを「接続待ち時間」と「クエリ実行時間」の2つに分けて計測することです。大部分が "待ち" ならプールがボトルネックです。大部分が "クエリ内" なら SQL とインデックスに注力します。

便利な確認事項:

  • プール統計(open, in-use, idle)をログに出し、in-use が max に張り付いていないか監視する。
  • 接続取得にタイムアウトを入れて、ステージングで待ちがすぐに失敗するようにする。
  • Postgres のアクティブ接続数と max_connections までの余裕を監視する。
  • 各リクエストが rows を閉じて接続を速やかに返していることを確認する。
  • 実際に運用する予定のインスタンス数で負荷テストを行う。

pgxpool と database/sql の違い

pgxpool は Postgres に最適化されたプールで明確な統計と良いデフォルトが得られます。database/sql は DB 間で共通のインターフェースを提供しますが、プール設定やドライバの挙動を明示的に調整する必要があります。

実用ルール:Postgres に集中して直接制御したければ pgxpool が使いやすいことが多いです。ライブラリが database/sql を期待しているならそちらに従い、プール設定を明確にして待ち時間を測ってください。

例:注文一覧を返すエンドポイントが単体では 20 ms なのに 100 同時ユーザーで 2 s まで跳ね上がる。ログで 1.9 s が接続待ちになっているなら、クエリチューニングでは改善せず、プールと Postgres 側の総接続数の調整が先です。

クエリプランの読み方:EXPLAIN を素早く眺める

エンドポイントが遅ければ、Postgres が実際に何をしているかを確認してください。EXPLAIN を素早く読むだけで数分で解決策が見つかることが多いです。

API が送る正確な SQL に対して次を実行します:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;

いくつかの行を重点的に見ます。Postgres が選んだトップノードと下部の合計時間(実行時間)を確認し、推定行数と実際の行数の差を比べてください。大きなギャップはプランの誤推定を示します。

主要行の読み方

  • Index Scan や Index Only Scan があればインデックスを使っており、通常は良い兆候です。
  • Bitmap Heap Scan は中程度のマッチサイズでは悪くありません。
  • Seq Scan はテーブル全体を読んでいることを示し、テーブルが大きい場合は問題です。

一般的なレッドフラグ:

  • 大きなテーブルでの Seq Scan
  • 推定行数と実際の行数の大きな乖離(例:10 推定 に対して 10,000 実際)
  • ORDER BY と組になったソートが時間の大半を占めている
  • スキャン後に多くの行が Filter で除外されている
  • BUFFERS の shared read が高い(大量のデータ読み込み)

プランが狂う原因(と簡単な対処)

遅いプランは通常、いくつかのパターンから生じます:

  • WHERE と ORDER BY の組み合わせに合わないインデックス(例:(user_id, status, created_at) のような複合)
  • 型不一致(UUID 列に対してテキストパラメータで比較しているなど)がインデックス利用を妨げる
  • WHERE に関数が入っている(例:WHERE lower(email) = $1)とインデックスが使われないことがある(表現インデックスで対応可能)

プランが変に見えて推定が大きく外れている場合、統計が古いことが多いです。ANALYZE を実行するか autovacuum が追いつくのを待たせて、Postgres に最新の行数や値の分布を学習させてください。大量のインポート後や新しいエンドポイントが急に多くの書き込みを始めたときに特に重要です。

実際に動くクエリに合わせたインデックス設計

ロールバック準備で出荷
Go サービスをデプロイし、スナップショットとロールバックでパフォーマンス調整を安全に行えます。
アプリをデプロイ

インデックスは、実際に API が投げる問いに合っているときにのみ役立ちます。推測だけで作ると、書き込みが遅くなり、ストレージが増え、ほとんど速度向上が得られないことがあります。

考え方としては:インデックスは特定の質問への近道です。API が別の質問をしているなら Postgres はその近道を使いません。

フィルタ+ソートに合わせてインデックスを作る

エンドポイントが account_id で絞って created_at DESC でソートするなら、1つの複合インデックスは別々のインデックスより優れることが多いです。必要な行を見つけ、正しい順序で返す作業を軽くできます。

指針:

  • よくフィルタする列をインデックス化し、次にソート列を置く。
  • 複合インデックスは小さく保つ。2列が一般的、3列は場合による。4列以上は注意。
  • 最も選択率が高い(結果を最も絞る)列を先頭に置く。
  • 別々のインデックスが複合インデックスでカバーされるなら、別のインデックスは避ける。
  • 「たぶん役に立つ」複数のインデックスより、一つの良く選ばれたインデックスを推奨。

例:GET /orders?status=paid で常に新しい順に表示するなら (status, created_at DESC) が良い適合です。大部分のクエリが customer も絞るなら (customer_id, status, created_at) が合う場合もありますが、本番でそのエンドポイントが実際にどう実行されているかに依存します。

よく使われるフィルタ向けのパーシャルインデックス

トラフィックの大半が狭いスライスに集中するなら、パーシャルインデックスの方が安く速くなることがあります。たとえばアクティブなレコードだけを主に読むなら、WHERE active = true のインデックスは小さくメモリに残りやすくなります。

インデックスの有効性を確かめるには:

  • EXPLAIN(安全な環境では EXPLAIN ANALYZE)を実行し、クエリに対してインデックススキャンが使われているか確認する。
  • インデックスの有無でタイミングと読み込んだ行数を比較する。
  • "Rows Removed by Filter" が高いままならインデックスがフィルタに合っていない可能性がある。

未使用のインデックスは慎重に削除してください。使用統計(インデックスがスキャンされたかどうか)を確認し、低リスクな時間帯に1つずつドロップし、ロールバックプランを用意しましょう。未使用のインデックスは無害ではありません。書き込み時にすべての更新で余分な作業が増えます。

時間経過で遅くならないページング設計

ページングは速い API が遅く感じられ始めるポイントです。ページングは UI の細部ではなくクエリ設計の問題として扱ってください。

LIMIT/OFFSET が遅くなる理由

LIMIT/OFFSET は見た目は簡単ですが、深いページはコストが増えます。Postgres はスキップする行を辿る必要があり(多くの場合ソートも)、1ページ目は数十行で済んでも、500ページ目は数万行をスキャンして捨てる必要が出ます。

また、リクエスト間で行が挿入・削除されると結果が不安定になり、ユーザーは重複や欠落を経験するかもしれません。

キーセットページング(カーソル)の「最後に見た」例

キーセットページングは "最後に見た行の次の20行をください" という問い方をします。これにより DB は小さく一貫したスライスを扱います。

単純な例(増分 id の場合):

SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;

API は next_cursor としてページの最後の id を返します。次のリクエストはその値を $1 に使います。

時系列ソートでは安定した順序とタイブレーカーが必要です。created_at だけでは同じタイムスタンプを持つ行があると不十分なので複合カーソルを使います:

WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

重複や欠落を防ぐためのルール:

  • ORDER BY に一意のタイブレーカー(通常は id)を必ず含める。
  • リクエスト間でソート順を同一に保つ。
  • カーソルはクライアントに対して不透明にする(created_at と id を一緒にエンコードする)。
  • ユーザーがフィルタできるなら、各ページで同一のフィルタを適用する。
  • 可能なら不変のソートフィールド(作成時間)を可変のもの(status や score)より優先する。

JSON 形成:小さいペイロードで高速レスポンス

DB プールを適切化する
妥当なプール制限とタイムアウトを含むコードを生成し、自信を持って負荷を試せます。
プロジェクト開始

驚くほどよくある遅延原因はデータベースではなくレスポンスそのものです。大きな JSON は組み立てに時間がかかり、送信に時間がかかり、クライアント側のパースにも時間がかかります。最も簡単な改善は返す量を減らすことです。

まず SELECT を見直してください。エンドポイントが id, name, status のみを必要とするなら、その列だけを要求します。SELECT * はテーブルに長いテキストや JSON、監査列が増えると静かに重くなります。

もうひとつの遅延原因は N+1 パターンです:50 件のリストを取ってから関連データのために 50 回追加クエリを実行する。テストは通っても本番負荷で崩壊します。慎重な JOIN や ID バッチの 2 クエリ構成を優先してください。

ペイロードを小さく保つための方法:

  • include= フラグや fields= マスクを使って一覧は軽く、詳細はオプトインにする。
  • ネスト配列を上位 N 件に制限し、全履歴は別エンドポイントにする。
  • クライアントが一部のキーだけ必要なら内部 JSON 列をそのまま返さない。
  • 長いラベルを繰り返す代わりに短いコードを使う。

Postgres 側で JSON を作るか、Go 側で作るか?

どちらも高速にできます。最適化したい点で選んでください。

  • Postgres の JSON 関数(jsonb_build_object, json_agg)はラウンドトリップを減らし、一つのクエリで予測可能な形を返すときに便利です。
  • Go 側で組み立てるのは条件ロジックや再利用する構造体がある場合、SQL を読みやすく保ちたい場合に有利です。

良いルールは:Postgres にフィルタ・ソート・集計を任せ、最終的な表現は Go 側で行うことです。

迅速に API を生成する場合(たとえば Koder.ai を使うとき)は、初期から include フラグを入れておくと、後でエンドポイントが肥大化するのを防げます。これによりフィールド追加が安全になり、すべてのレスポンスが重くなるのを避けられます。

最初のユーザー到着前に行うステップバイステップのチューニング

大きな検証ラボは不要です。短い、繰り返し可能な手順で多くの障害になりうる問題を発見できます。生成コードを出発点にする場合、トラフィックが増えたら障害になる問題を事前に洗い出すことが重要です。

何かを変える前に小さなベースラインを書き留めてください:

  • 最も忙しいエンドポイントの p95 と p99
  • エラーレートとタイムアウト
  • データベースの CPU とアクティブ接続数
  • 合計時間で見た上位5つの遅いクエリ(単一の最悪だけでなく)

チューニングパス

小さく始め、1 回に 1 つだけ変え、変更後に再テストします。

  1. 実際の利用に近い 10〜15 分の負荷テストを実行します。最初のユーザーが触るであろうエンドポイント(ログイン、一覧、検索、作成)を攻め、ルートを p95 と総時間でソートします。

  2. SQL 調整の前に接続圧をチェックします。プールが大きすぎると Postgres を圧倒するし、小さすぎると長い待ちが発生します。接続取得待ち時間と接続数のスパイクを見て、まずプールと idle 制限を調整し、同じ負荷を再実行します。

  3. 最も遅いクエリに対して EXPLAIN を実行し、最大のレッドフラグを修正します。典型的なのは大テーブルのフルスキャン、巨大なソート、行数が爆発する JOIN です。最も悪いクエリを選んで「普通」にすることを目指します。

  4. インデックスを 1 つ追加または調整し、再テストします。インデックスは WHERE と ORDER BY に合うと効果的です。一度に5つも追加しないでください。

  5. レスポンスとページングを引き締め、再テストします。50 行の大きな JSON を返しているなら DB・ネットワーク・クライアントがすべて負担を負います。UI が必要とする列だけを返し、テーブルが成長しても遅くならないページングを選んでください。

シンプルな変更ログを残してください:何を変えたか、なぜか、p95 がどう動いたか。変化が改善をもたらさなければ戻して次に進みます。

避けるべきよくあるミスと罠

構築前にパフォーマンスを計画
コード生成の前にフィルター、ソート、ページングを決めるための Planning Mode を使いましょう。
プランナーを開く

多くの Go+Postgres のパフォーマンス問題は自己責任で発生します。良いニュースは、いくつかのチェックで本番トラフィック前に多くを防げることです。

古典的な罠はプールサイズを "できるだけ大きく" することです。大きくすればするほど遅くなることがあり、Postgres はセッション間の管理に時間を使い、アプリは波状的にタイムアウトを出します。小さく安定したプールで予測可能な並列性を保つ方が勝つことが多いです。

もう一つの間違いは "全部にインデックスを張る" ことです。読み取りの助けにはなりますが、書き込みを遅くし、クエリプランを思わぬ形で変えることがあります。追加前後で計測し、インデックス追加後はプランを再確認してください。

ページングの負債は静かに忍び寄ります。Offset ベースは早期には問題ないように見えても、時間経過で p95 が上がります。

JSON ペイロードのサイズも隠れた負擔です。圧縮は帯域を助けますが、構築・割当・パースのコストはなくなりません。フィールドを削り、深いネストを避け、画面が本当に必要とするものだけを返してください。

平均応答時間だけを見ていると、実際のユーザー痛点を見逃します。p95(場合によっては p99)がプール飽和、ロック待ち、遅いプランを最初に示します。

プレローンチでの簡単な自己チェック:

  • 小さな負荷テストでプール待ち時間と Postgres 接続数を監視する。
  • 同一エンドポイントで平均と p95 を比較する。
  • テーブルが 10 倍になってもページングが劣化しないか検証する。
  • 一覧エンドポイントのレスポンスサイズ(バイト数)を確認する。
  • インデックス追加やフィルタ変更後に EXPLAIN を再実行する。

ローンチ前の簡単チェックリストと次の一手

実ユーザーが来る前に、ストレス下で API が予測可能に動く証拠を欲します。目的は完璧な数値ではなく、タイムアウトやスパイク、本当に仕事を受け付けなくなる DB を招く問題を捕まえることです。

ステージング環境(本番に近い DB サイズ、同じインデックス、同じプール設定)でチェックを行ってください:主要ルートごとの p95 を負荷下で計測し、合計時間で上位のスロークエリを収集し、最悪クエリに対して EXPLAIN (ANALYZE, BUFFERS) を行い期待するインデックスが使われているか確認し、最も忙しいルートのペイロードサイズを簡易検査します。

その後、プロダクトが壊れる様子を模したワーストケースを一回実行します:深いページを要求し、最も広いフィルタを適用し、コールドスタート(API を再起動して同じリクエストを最初に投げる)で試す。深いページングがページを進むごとに遅くなるなら、ローンチ前にカーソルベースページングに切り替えてください。

デフォルトを文書化してチームが後で一貫した選択をできるようにします:プール制限とタイムアウト、ページングルール(最大ページサイズ、offset を許可するか、カーソル形式)、クエリルール(必要な列のみ選択、SELECT * を避ける、コストの高いフィルタを制限)、ログルール(スロークエリ閾値、サンプル保持期間、エンドポイントのラベリング)。

Koder.ai で Go + Postgres サービスを生成・エクスポートする場合、デプロイ前に短いプランニングを行うとフィルタ、ページング、レスポンス形状を意図的に設計できます。インデックスやクエリ形状のチューニングを始めたら、スナップショットとロールバックで "助けになるが他を壊す" 修正を元に戻せるようにしておくと安全です。Koder.ai(koder.ai)上では、チャットでこれらのサービスを生成して改善し、準備ができたらソースをエクスポートするワークフローが想定されています。

よくある質問

Go API が遅いのは Postgres のせいか、自分のコードのせいかを素早く見分けるには?

まず DB 待機時間 と アプリ側の処理時間 を分離して計測します。

  • データベースが原因なら、ハンドラは主にクエリを待っている状態になります。Go の CPU 使用率は普通のままリクエストが「進行中」で溜まることがよくあります。
  • アプリ側が原因なら、クエリは速やかに返るが、オブジェクト構築、行ごとの追加クエリ、大きな JSON のマーシャリング、ログ処理などで時間がかかります。Go の CPU とメモリがレスポンスサイズに応じて上がるのが特徴です。

「接続を待っている時間」と「クエリ実行時間」を簡単に計測して、どちらが支配的かを見てください。

チューニング前にまず追うべきメトリクスは何ですか?

再現可能な小さなベースラインを作ってください:

  • 主要エンドポイントの p95 レイテンシ(平均ではなく)
  • エラーレート(5xx、タイムアウト、キャンセル)
  • リクエストあたりの DB 時間(Postgres を待っている時間)

例として「50 同時ユーザー時に p95 が 200 ms 未満、エラー率は 0.5% 未満」のように声に出して言える目標を決めます。変更は一度に一つずつ行い、同じリクエスト構成で再テストしてください。

Postgres のスロークエリログは有効にすべきか?実用的な閾値は?

プレローンチのテストでは低めの閾値(例:100–200 ms)でスロークエリログを有効にし、フルステートメントをログに残すと SQL クライアントへコピーして調査しやすくなります。

ただし本番でずっと有効にするのは避けてください:

  • ノイズが多くなります。
  • ログ量が増えるとオーバーヘッドになります。

最悪のクエリを見つけたら、サンプリングに切り替えるか閾値を上げます。

Go API と Postgres の良い接続プール設定の出発点は?

実用的なデフォルトは API インスタンスの CPU コア数の小さな倍数 です。多くの小規模 API では max open connections を 5〜20 の範囲で始め、idle も同様に保ち、接続を 30〜60 分ごとに再生するのが安全です。

失敗モード:

  • プールが小さすぎる:リクエストが接続を待っている間にレイテンシが上がるが、Postgres のクエリ時間は問題ないことが多い。
  • プールが大きすぎる:多くのアクティブセッションで Postgres に負荷がかかり、メモリやレイテンシが悪化する。

注意:プールはインスタンス数で掛け算されます(20 接続 × 10 インスタンス = 200 接続)。

接続プールがボトルネックかどうかを確認する方法は?

DB 呼び出しを2つに分けて計測します:

  • 接続取得待ち時間(プール待機)
  • クエリ実行時間(Postgres の処理)

大部分が接続待ちならプールがボトルネックです。インスタンス数やプールサイズ、タイムアウトを調整してください。クエリ実行が大部分なら EXPLAIN とインデックスに注力します。

また、rows を確実に Close して接続を素早く返していることを確認してください。

エンドポイントが遅いとき、EXPLAIN でまず何を見ればいい?

EXPLAIN (ANALYZE, BUFFERS) を API が投げている正確な SQL に対して実行し、次を確認します:

  • 大きなテーブルでの Seq Scan
  • 推定行数と実際の行数の大きな差
  • ORDER BY に伴う Sort が時間を支配している
  • フィルタで大量の行が取り除かれている(Rows Removed by Filter)
  • BUFFERS の共有読み取りが多い(大量のデータ読み込み)

最上位ノード(Postgres が選んだ実行ノード)と下部の合計時間を見て、問題点を特定してください。

フィルタとソートのある一覧エンドポイントに対して正しいインデックスを選ぶには?

エンドポイントが実際に行うクエリに合わせてインデックスを作ること。WHERE と ORDER BY の組み合わせに合う複合インデックスが単体インデックスより有効なことが多いです。

実務上の指針:

  • よく使うフィルタ列を先に、ソート列を後に置く。
  • 複合インデックスは小さく保つ(2列が一般的、3列は場合による)。
  • 最も絞り込む列を先頭に置く。
  • 別々のインデックスが複合インデックスでカバーされるなら、別のインデックスは不要。

例: で絞って で新しい順に返すなら が合うことが多いです。

Postgres でパーシャルインデックスはいつ有効か?

トラフィックの大部分がテーブルの狭いスライスに集中するなら、パーシャルインデックスが有効です。例えば active = true の行がほとんど読み出されるなら、WHERE active = true のインデックスは小さくメモリに入りやすく、書き込み負荷も抑えられます。

使う前に EXPLAIN でそのインデックスが本当に選ばれるかを確認してください。

なぜ LIMIT/OFFSET が時間とともに遅くなるのか、代わりに何を使うべきか?

LIMIT/OFFSET は深いページで遅くなります。Postgres はスキップする行を辿る必要があり、ページ番号が深くなるとコストが上がるからです。また、挿入や削除によって結果が不安定になりやすく、重複や欠落が出ることがあります。

代わりに キーセット(カーソル)ページング を推奨します:

  • 安定したソートと一意のタイブレーカー(通常は id)を使う。
  • すべてのリクエストで同じ ORDER BY を使う。
  • (created_at, id) のような複合カーソルをエンコードして返す。

これにより各ページのコストはテーブル成長によらずほぼ一定になります。

DB クエリは速いのにレスポンスが遅い場合、JSON ペイロードを削るべきか?

通常ははい、一覧系のエンドポイントではペイロードを減らすべきです。送らないレスポンスが最速です。

実用的な対策:

  • 必要な列だけを SELECT する(SELECT * を避ける)。
  • クライアントが重いフィールドを要求する時だけ返す include= や fields= を用意する。
  • ネスト配列は上位 N 件だけに制限し、フル履歴は別エンドポイントで提供する。
  • N+1 問題を避け、JOIN か ID バッチでまとめて取得する。
目次
Go API が Postgres 上で「遅い」と感じるときの様子まず基準を作る:重要な数値Postgres を安定させる接続プーリングクエリプランの読み方:EXPLAIN を素早く眺める実際に動くクエリに合わせたインデックス設計時間経過で遅くならないページング設計JSON 形成:小さいペイロードで高速レスポンス最初のユーザー到着前に行うステップバイステップのチューニング避けるべきよくあるミスと罠ローンチ前の簡単チェックリストと次の一手よくある質問
共有
Koder.ai
Koderで自分のアプリを作ろう 今すぐ!

Koderの力を理解する最良の方法は、自分で体験することです。

無料で始めるデモを予約
user_id
created_at DESC
(user_id, created_at DESC)

これだけで Go の CPU/メモリ負荷やテールレイテンシがかなり改善することが多いです。