PostgreSQLのカーソル、ちゃんと使えてる?メモリを爆発させないための「一行ずつ」の作法
やあ。データベースを触っていると、たまにこういう場面に出くわさないか?
「数百万件あるテーブルを全件スキャンして、複雑なロジックで計算してから別のテーブルに書き込みたい」
そんなとき、安易に `SELECT FROM big_table` を実行して、アプリケーション側で全部メモリに載せようとすると……まあ、大抵の場合はメモリ不足でプロセスが死ぬか、サーバーが悲鳴を上げるのがオチだよね。
PostgreSQLのストアドプロシージャ(PL/pgSQL)を書くとき、この「巨大な結果セットをどう捌くか」という課題に対する強力な武器が、そう、「カーソル」だ。今日は、実務で明日から使えるカーソルの扱い方について、ちょっとしたコツを交えて話していこうと思う。
—
なぜ「カーソル」が必要なのか?
PL/pgSQLで `FOR record IN SELECT …` という書き方を見たことがあるかもしれない。あれは便利だけど、実は裏側で結果セット全体を一旦メモリにロードしようとする性質がある。件数が少ないうちはいいけれど、数百万行レベルになると、PostgreSQLのバックエンドプロセスのメモリを圧迫して、パフォーマンスがガタ落ちになるんだ。
カーソルは、「結果セットへのポインタを保持し、必要な分だけをメモリに引き出す」という仕組みだ。これを使えば、どれだけ巨大なデータセット相手でも、メモリ消費量を一定に保ったまま処理を完結できる。
—
カーソルの基本サイクル:4つのステップ
カーソルの扱いは、料理の準備と片付けに似ている。この4つのステップを体に叩き込んでおこう。
1. DECLARE: カーソルに名前とクエリを紐付ける。
2. OPEN: クエリを実行し、ポインタをセットする。
3. FETCH: 一行ずつ(あるいは数行ずつ)データを取り出す。
4. CLOSE: 使い終わったら必ず閉じる。
実践コード:こんな風に書く
DO $$
DECLARE
— 1. カーソルの宣言
cur_users CURSOR FOR SELECT id, username FROM users WHERE is_active = true;
rec RECORD;
BEGIN
— 2. カーソルのオープン
OPEN cur_users;
LOOP
— 3. FETCHで一行ずつ取り出す
FETCH cur_users INTO rec;
— データがなくなったらループを抜ける
EXIT WHEN NOT FOUND;
— ここで好きな処理をする
RAISE NOTICE ‘処理中: %’, rec.username;
END LOOP;
— 4. クローズ
CLOSE cur_users;
END $$;
—
実務で「おっ、分かってるね」と思われるポイント
教科書通りならこれで終わりなんだけど、現場で使うならもう少しだけ踏み込んでおこう。
1. FORループの「カーソル版」を使う
実は、わざわざ `OPEN` や `CLOSE` を手書きしなくても、PostgreSQLは便利な構文を用意してくれているんだ。
FOR rec IN cur_users LOOP
— これならOPEN/CLOSEを自動でやってくれる!
— しかもコードが圧倒的に読みやすい。
RAISE NOTICE ‘ユーザーID: %’, rec.id;
END LOOP;
基本的にはこの書き方で十分だ。ただし、カーソルの動的な制御(パラメータを渡したい、特定の条件下でスキップしたいなど)が必要なときは、明示的な `OPEN` を使うのがベターだね。
2. トランザクションを意識する
カーソルは基本的にトランザクション内でのみ有効だ。もし `COMMIT` を発行すると、その瞬間にカーソルは無効化される。だから、数百万件を処理するためにカーソルを使う場合は、ループの中で小まめにコミットするような実装はできない(というか、してはいけない)。
「大きな処理をどう小分けにするか」という設計の話になると、また別の機会が必要だけど、まずは「カーソルは一つのトランザクションと運命共同体」と覚えておいてくれ。
—
最後に:エンジニアとしての心得
「ループ処理」というのは、SQLの集合演算(`UPDATE … WHERE …` のような一括処理)に比べれば、どうしても低速になりがちだ。
だから、僕が後輩にいつも言っているのはこうだ。
「まずは集合演算で一気に片付けられないか、3分だけ考えてみてくれ。それでも無理なら、カーソルを使おう」
カーソルは便利だけど、多用すればいいってもんじゃない。適材適所。その判断ができるのが、いいデータベースエンジニアだと思うよ。
さて、今日の解説はここまで。もし「もっと複雑な条件でカーソルを回したい」「パフォーマンスがどうしても出ない」なんて悩みがあれば、またいつでも聞いてくれ。現場からは以上だ!
コメント