【実務・中級編】 cursor_tuple_fractionの調整 – PostgreSQL

「FETCH FIRST 10 ROWS」でクエリが遅い? PostgreSQLの隠れた調整弁 `cursor_tuple_fraction` の話

やあ。最近、PostgreSQLのパフォーマンスチューニングでこんな経験はないかな?

「アプリ側で `LIMIT 10` をつけているのに、実行計画を見ると、なぜか重いフルスキャン(Seq Scan)を選択していて、結果が返ってくるまで数秒待たされる……」

これ、PostgreSQLを長く触っていると一度はぶち当たる壁なんだよね。インデックスも貼ってあるし、`LIMIT` も効いているはずなのに、オプティマイザがなぜか「全件スキャンしてソートしたほうが早い」と判断しちゃう。

実はこれ、PostgreSQLのオプティマイザが「カーソルでどれくらいの行数を取り出すか」を計算する際の、ある「決め打ち」が原因であることが多いんだ。今日はその正体である `cursor_tuple_fraction` について、現場目線で解説するよ。

—

「全件取得」を前提としたプランニングの罠

PostgreSQLのオプティマイザは、クエリの実行コストを計算するときに「このクエリは結果の何割くらいを読むのか?」という仮定を置くんだ。

ここで登場するのが `cursor_tuple_fraction` という設定項目。デフォルト値は `0.1`(10%)になっている。

これはどういうことかというと、「クエリがカーソル経由で実行された場合、とりあえず結果セットの最初の10%分くらいは読むだろう」という前提でコストを算出する、というものなんだ。

問題はここからだよ。
例えば、100万件あるテーブルに対して `SELECT FROM users ORDER BY created_at DESC LIMIT 10;` を投げたとしよう。
理論上、インデックスがあれば数件読むだけで済むはずだよね? でも、PostgreSQLはデフォルトの `0.1` を基準にするから、「100万件の10%である10万件分を処理するコスト」を見積もってしまうことがあるんだ。

その結果、「10万件も読むなら、インデックスを引くよりテーブルを全部舐めてクイックソートしたほうが早いな」という、「全件取得前提」の歪んだプランが生成されてしまうわけ。これが、`LIMIT` をつけても遅いクエリの正体だ。

—

実践:どう調整するのか?

この挙動を改善するには、セッションレベルで `cursor_tuple_fraction` を調整するのが一番手っ取り早い。

例えば、Webアプリの特定のエンドポイントなど、「常に数件しか取らない」と分かっている処理があるなら、このように設定してみよう。

— トランザクション内、あるいはセッション単位で設定
SET cursor_tuple_fraction = 0.0001;

— その後にクエリを実行
SELECT FROM users ORDER BY created_at DESC LIMIT 10;

こうすることで、「100万件の10%」ではなく「100万件の0.01%(=100件)」を基準にコスト計算してくれるようになる。これでオプティマイザは「あ、これならインデックスを辿るコストの方が圧倒的に低いな」と気づいてくれるはずだ。

—

注意点:魔法の杖ではない

もちろん、これを闇雲に全域で適用するのはおすすめしない。

  • 汎用性が下がる: この設定をグローバル(`postgresql.conf`)で極端に小さくしてしまうと、逆に「全件取得したい」というクエリまでインデックスを無理やり使うようになり、全体的なパフォーマンスが悪化する可能性がある。
  • 統計情報の鮮度: `cursor_tuple_fraction` をいじる前に、まずは `ANALYZE` が適切に走っているか、統計情報が古くないかを疑うのがエンジニアの鉄則だ。

僕が現場でよくやるのは、「特定の重いバッチや、どうしても遅いAPIエンドポイントの先頭でだけ設定する」というアプローチだね。

まとめ

`cursor_tuple_fraction` は、いわばオプティマイザの「先入観」を矯正するパラメータだ。

1. `LIMIT` をつけているのにフルスキャンが走っていたら、まずはここを疑う。
2. `SET` で値を小さくして、実行計画(`EXPLAIN`)がどう変わるか見てみる。
3. インデックス・スキャンが選ばれるようになったら大成功。

データベースはブラックボックスじゃない。オプティマイザが「なぜその判断をしたのか」という思考回路をトレースできるようになると、チューニングは一気に面白くなるよ。

もし君のプロジェクトで「どうしても実行計画がうまく倒れない」というクエリがあったら、一度試してみてくれ。健闘を祈る!

コメント

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