世界を動かす技術を、日本語で。

PostgresのLISTEN/NOTIFYは実際にスケールします

2026年7月25日原文(dbos.dev)

概要

  • PostgresLISTEN/NOTIFY はスケーラビリティに関する誤解が多い機能。
  • 低レイテンシー通知ストリーム 用途で非常に有用。
  • 実際のボトルネックは グローバルロック によるもの。
  • バッファリングとバッチ化 で大幅な性能改善が可能。
  • 最適化により 6万件/秒 のスループットと ミリ秒単位 の遅延を実現。

LISTEN/NOTIFYのスケーラビリティ問題

  • PostgresのLISTEN/NOTIFY は、通知ベースのストリームやpub/subに利用可能な強力な仕組み。
  • 一部ブログ記事により「スケールしない」との悪評が広まった経緯。
  • 問題の本質は 直感的でない性能特性ドキュメント未記載の挙動
  • NOTIFY はグローバルロックを利用し、これがスケーラビリティの障害となる要因。
  • 「直感的でない挙動」と「スケールしない」は同義ではない点に注意。

Postgresストリーム設計の基本

  • ストリーム用のテーブルを作成し、各チャンク(例:LLMのレスポンストークン)を新規行として保存。
  • 読み取り側は、次のチャンク到着時期が不明なため、ポーリング方式を採用しがち。
    • ポーリング間隔が長いとレイテンシー増大。
    • 短いと同時ポーラーがDB負荷を増大。
  • LISTEN/NOTIFY により、リソースを無駄に消費せず、即座に通知受信が可能。

LISTEN/NOTIFYのパフォーマンス問題

  • 初期実装では、ストリームテーブルのトリガーで毎回NOTIFYを発行。
  • リーダーは通知待機し、通知到着で即座にデータ取得。
  • 正しく低レイテンシーだが、スループットは 2,900件/秒 で頭打ち。
  • CPUやIOPS等のリソース消費は目立たず、ボトルネックは グローバルロック

グローバルロックの仕組み

  • NOTIFYを含むトランザクションのコミット時 に、グローバル排他ロックを取得。
  • このロックは、トランザクションが完全にコミットされ、fsync()でディスクに反映されるまで保持。
  • 通知の順序保証のため、内部キューにコミット順で通知を格納する必要。
  • トランザクションのコミット順はコミット完了時に決定されるため、排他ロックで直列化が必要。
  • このため、 各書き込みでロック取得→直列化→スループット低下 という構造。

パッチの現状と限界

  • Postgres 19向けのパッチでは、通知チャネルが多い場合の効率化のみが対象。
  • グローバルロック自体や本質的なボトルネックは未解決。

パフォーマンス最適化手法

  • 多くのユースケースでは、通知自体が「真実のソース」ではなく、単なる「新データ到着の合図」。
  • 通知の順序や完全な永続性が不要 な場合、NOTIFYを メモリバッファでバッチ化 し、一定間隔でまとめて発行。
  • バッファフラッシュ時のみグローバルロック取得。
    • 各書き込みは高速化、Postgresのグループコミット等の最適化も活用可能。
  • バッファ中にプロセスがクラッシュした場合、通知が失われるリスク。
    • 対策として、リーダーは通知待機+一定間隔でバックアップ的なポーリングを実施。
    • ポーリング頻度は低く抑えられるため、DB負荷増加は限定的。

最適化の効果とベンチマーク

  • 最適化後 は、同時リーダー存在下で 6万件/秒 のストリーム書き込みを達成。
  • レイテンシーは 15~100ms に抑制。
  • 最大スループット時には CPUが完全に利用されている ことを確認、ボトルネック解消。

参考リンク・コミュニティ

Hackerたちの意見

最初のリリースでLISTEN/NOTIFYをサポートしていた時、パフォーマンスの問題があったのを覚えてる(確かロックが悪かったと思う)。今日ここで言及されている「悪い投稿」は、その最初の段落のすぐ後に訂正が載ってるね。訂正が5月8日の日付らしいから、7月24日の投稿では、この機能がスケールしないって主張してた人気の投稿が、悪意があったわけじゃないし、その時の主張が間違ってたわけでもないってことを認めた方がいいかも。

Postgres 19に来る最適化のことを言ってるなら、元の投稿で触れられてるよね。 > ちなみに、この問題に関連するPostgresのパッチについてオンラインで少し議論があった。 このパッチ(Postgres 19でリリース予定)は、グローバルロックを削除したり、観察されたボトルネックを修正したりするものではない。むしろ、多くの通知チャネルがあり、各リスナーが特定のチャネルだけを待っている狭いケースを最適化している。

明確にスケールしない一つの理由(最後に確認した時から何か変わったかもしれないけど、サクッと調べた限りでは)は、通知のデータサイズが8000バイトに制限されてることだね。通知が行を持つ意味がない場合(IDだけで済むなら)、使いづらくなる。俺はウェブゲームを作ってて、イベントは状態の変化を一時的に説明するもので、データベースに保存する意味がなかったし、それ以上のサイズになることもあったから、その用途には合わなかった。

スケーラブルな通知システムを実装する者として、通知サイズには上限を設けるべきだと思う。 - メッセージをO(1)に保つことで、通知の量のスケーリングに集中できる - これに遭遇した人には、 - 自分は恣意的に大きなメッセージを計画していないってことを伝えられるし、そうしないとパフォーマンスが止まるかもしれない - 彼らが私の通知システムを誤用しているかもしれないってことも伝えられる

「スケール」ってのはバイナリじゃなくて、連続体なんだよね。「60K/sにスケールする」ってのは、あるシステムには5桁も多すぎるし、別のシステムには5桁も足りないってこともある。個人的には「早すぎる最適化」を「最も一般的な開発者のミス」のリストから外して、「間違ったスケーリングファクターを持つ技術を使うこと」を入れたい。小さすぎるものを使って必要以上に超えたら失敗は明白だけど、その逆も問題なんだよね。超スケーラブルな技術のオーバーヘッドや管理の問題、スケールするために課される制限を、実際にはもっとリッチなモデルの方が良いシステムに持ち込むのは悪い選択だと思う。LISTEN/NOTIFYの上限は小さいから注意が必要で、俺は最も悲観的な負荷数値を考慮しても、少なくとも1桁の余裕を持っていたいと思ってるけど、それでも多くのプロジェクトには十分だし、DB全体との統合や可用性、他のサービスを運用する必要がないことを考えると、単純に選択肢から外すべきではないと思う。彼らが引用している元の2K/sの数値も、メッセージ数が秒単位で測られるシステムにはかなりの量だよ。

もう一方には5桁も小さい。良い投稿に対する細かい指摘だけど、6億RPSのシステムなんてあまりないと思うし、存在するものはほぼ間違いなく特注の目的に特化したツールを使ってるだろうね。

もし60K/s未満を想定していて、突然20K/sから200K/sに向かうことになったら、1M/sを想定して実際の負荷が20K/sの方がいい問題だと思う。前者の予期しない成功は、たくさんのバンドエイドやスケーリングにお金をかけることになるけど、後者はコスト構造や初期投資に縛られちゃうからね。個人的には、実際に予想されるスケールに対して適度なマージンを持って設計するべきだと思う。これを超えるのは、比較的「無料」でできるときだけでいいよ。(数千円で大きなハードウェアを買えるなら、スケーラビリティ以外は同じ解決策なら大きい方を選ぶべき。)

DBOSがPostgres(今はSQLiteも)をうまく活用しているところが大好き。既存のCRUDスタックに簡単に組み込めるし。「耐久性のあるワークフロー」の道に進み始めると、あちこちでそれを目にするようになる。最近の実験では、個々のメールを耐久性のあるワークフローとして扱っていて、コミュニケーションを取る人たちや、GitHubやAttioのようなエージェントやツールがフローの中で順番に関わっている。 https://housecat.com/blog/gmail-durable-workflows-sandbox-vm

こういう投稿は、自分の問題、理解、解決策を独立して評価しているように思う。ツールのデフォルト設定で作業して、特定のパフォーマンスを期待しているなら、専門知識が不足しているとは言えない。誰もが失敗を通じて継続的に学んでいる。 1. 面白いのは、実験が96コア、384GB RAMのDBサーバーを使っているように見えること。これはどんな実験でも非常に重要な部分で、指摘されるべきだった。データベースは垂直スケーラブルで、それにも限界がある。 2. 誰が接続しているのか、どこから接続しているのかもパフォーマンスや全体のレイテンシに影響がある。 3. 60Kは大きな数字に見えるかもしれないが、実際にはシステムをダウンさせるのはトラフィックのバーストであって、通常のトラフィックではない。個人的には、大きなビジネスでない限り、そんな大きなサーバーから始めることはない。読み取りレプリカやクロスリージョンの冗長性を含めると、1つの本番DBクラスターのコストは10万以上になる。

まとめると、PostgresのNOTIFYは、アプリがデータベースに書き込まないようにすればスケールするってこと?それはちょっと厳しいな…

関連情報だと思うけど:PostgresのLISTEN/NOTIFYはスケールしない - https://news.ycombinator.com/item?id=44490510 - 2025年7月(コメント321件)

Digital Carrotの同期サーバーを設計してたときに、このプロセスを経たよ。結局、一番シンプルな解決策にすることにした。私の場合は、接続されたクライアント間で通知を送るためにメモリ内チャネルを使ったシンプルなGoのgRPCサービスなんだ。このシンプルなGoサーバーは、約2GBのRAMで1000人の同時接続顧客までスケールする。これ以上の有料顧客がいるとは思ってないし、もしそうなったら大きなVMを投入すればいいだけ。エンジニアは無限のスケーラビリティを求めて物事を複雑にしがちだけど、実際には解決しようとしている問題の範囲を理解するだけで、時間と労力を大幅に節約できるんだ。いつかこれが私にとっての問題になることを願ってるけど、それまではほとんどの人が心配する必要はないよね!

Hacker Newsで議論の続きを見る