4月29日、著者が「How Linux 7.0 Broke PostgreSQL: The Preemption Regression Explained」と題した記事を公開した。
Linuxカーネルの最新版で、PostgreSQLのパフォーマンスが従来の半分まで低下する深刻な問題が発覚した。96 vCPUの高性能サーバーで、トランザクション処理速度が98,565回/秒から50,751回/秒へと激減。原因は、Linuxカーネルのプロセススケジューリング方式変更が、PostgreSQLの内部ロック機構と予期しない相互作用を起こしたことにあった。
この問題は、クラウドインフラが急速に進歩する中で、OSレベルの変更がアプリケーション層に与える影響の複雑さを浮き彫りにしている。特に、AWS Graviton4のような最新プロセッサーでの大規模データベース運用において、従来の前提が通用しなくなる事例として注目される。近年、データベースのメモリ使用量は数百GBから数TBまで拡大しており、カーネルレベルの微細な変更が予想以上の影響を与えるケースが増加している。
AWSエンジニアが発見した深刻なパフォーマンス低下
2026年4月3日、AWSのエンジニアであるSalvatore Dipietroは、96 vCPUのGraviton4マシンでLinuxカーネル最新版を動かすと、PostgreSQLのスループットが従来版の約半分まで低下するというレポートをLinuxカーネルメーリングリストに投稿した。
Dipietroがpgbench(PostgreSQLの標準ベンチマークツール)で測定した結果は以下の通りだ:
- 従来版: 98,565 transactions/秒
- 最新版: 50,751 transactions/秒
この測定では、約8.47億行のテーブル(スケールファクター8,470)に対して1,024クライアント、96スレッドで高負荷な更新処理を実行している。現実の大規模Webサービスに近い負荷条件での測定だ。
問題の原因を探るため、DipietroがperfプロファイリングツールでCPU使用状況を分析したところ、衝撃的な結果が判明した:
|- 56.03% - StartReadBuffer
|- 55.93% - GetVictimBuffer
|- 55.93% - StrategyGetBuffer
|- 55.60% - s_lock # CPUの55%
**マシンのCPU時間の55%**が単一の関数s_lock内で費やされていた。これは明らかに異常な状況で、本来数ナノ秒で済むはずのロック処理に大部分のCPUリソースが消費されていることを意味する。
プリエンプションモデルの変更が招いた問題
Linuxカーネルの最新版では、プロセススケジューリング方式に重要な変更が加えられた。これまでサーバー向けのデフォルト設定だった**PREEMPT_NONE**(プリエンプション無効)が削除され、新しいPREEMPT_LAZYが導入されたのだ。
プリエンプションとは、実行中のスレッドを中断して他のスレッドにCPUを渡す仕組みのこと。従来は3つのオプションがあった:
PREEMPT_NONE: カーネルは実行中のスレッドをほぼ中断しない。従来のサーバーデフォルト設定で、コンテキストスイッチが少なく高スループットを実現PREEMPT_FULL: カーネルは安全なポイントでほぼいつでもスレッドを中断可能。レスポンス性を重視するデスクトップ向けデフォルトPREEMPT_LAZY: 割り込みは可能だが、自然な境界で待機を試みる折衷案
最新版ではPREEMPT_NONEが削除され、PREEMPT_FULLとPREEMPT_LAZYのみとなった。PREEMPT_LAZYはスループット重視のワークロードではPREEMPT_NONEの代替として設計されており、実際にほとんどのサーバーソフトウェアでは問題ない。しかし、PostgreSQLでは特定のケースで致命的な影響が出た。
この変更は、Linuxがリアルタイム性を重視する方向へシフトしていることを示している。コンテナ環境やマイクロサービスアーキテクチャの普及により、レスポンス性がより重要になったためだ。Real-Time Linuxの統合や、エッジコンピューティングでの低遅延要求に対応する流れの一環である。
PostgreSQLのメモリ管理とスピンロック
PostgreSQLは、データをデータページ(デフォルト8KB)という固定サイズの単位で管理する。ディスクからの読み込みは遅いため、PostgreSQLは**共有バッファープール**という大きな共有メモリ領域で最近読み込んだデータページをキャッシュしている。
各クライアント接続に対してPostgreSQLが生成するバックエンドプロセスが新しいデータページを必要とする場合、StrategyGetBuffer関数がバッファーを見つける役割を担う。この関数は、数百の並行バックエンドからのバッファープール アクセスを調整するためにスピンロックを使用する。
スピンロックは、ロックが利用可能になるまでスリープせず、タイトループで継続的にチェックし続ける仕組みだ。非常に短いクリティカルセクションでは、スレッドをスリープ・復帰させるオーバーヘッドより「スピン」の方が高速になる。PostgreSQLの開発者ドキュメントでも、スピンロックは「数十ナノ秒程度の非常に短い処理」でのみ使用すべきとされている。
スピンロックの前提は「ロック保持スレッドがすぐにロックを解放する」ことだ。20ナノ秒のクリティカルセクション中にスレッドがプリエンプトされることはない想定だった。
メモリページフォルトが引き起こす連鎖反応
問題の核心は、仮想メモリのページフォルト処理にある。PostgreSQLは起動時に共有バッファープールを大きな仮想メモリ領域として割り当てるが、Linuxは遅延割り当てを使用する。実際の物理ページは初回アクセス時にのみマッピングされ、この際にマイナーページフォルトが発生し、数マイクロ秒かかる。
Dipietroのベンチマークでは、共有バッファープールが120GBに設定されていた。デフォルトの4KBLinuxメモリページで計算すると、約3100万のメモリページ、つまり3100万回の潜在的な初回タッチページフォルトが存在することになる。
StrategyGetBuffer内で各バックエンドがスピンロックを取得してバッファープールの空きスロットを探す際、その共有メモリ領域へのアクセスがページフォルトを引き起こす可能性がある。スピンロック保持中にページフォルトが発生すると、以下の事態が起きる:
PREEMPT_NONE(従来版): バックエンドAがフォルト処理中でも、スケジューラーによる中断が起きにくく、フォルト解決後すぐにロックが解放されるPREEMPT_LAZY(最新版): スケジューラーがフォルト処理中のバックエンドAを中断し、別のプロセスを実行する可能性がある。バックエンドAの再開まで追加の待機時間が発生
この追加待機時間をtとすると、単にt分のCPU浪費ではなく、スピン中の全バックエンド数×tのCPU浪費となる。96 vCPUマシンで数百のバックエンドが動作している場合、この乗数効果は壊滅的だ。これが、ベンチマークでCPUの56%がs_lockで消費された理由である。
Huge Pagesによる解決策
幸い、PostgreSQLにはこの問題を克服するオプションがある:**Huge Pages**の使用だ。
デフォルトの4KBページの代わりに、カーネルがサポートする大きなページを使用する:
- 4KBページ: ~31,000,000回の潜在的ページフォルト
- 2MB Huge Pages: ~61,440回の潜在的ページフォルト
- 1GB Huge Pages: ~120回の潜在的ページフォルト
ページサイズを大きくすることで、ページフォルトの回数が劇的に減少し、TLB(Translation Lookaside Buffer)の負荷も軽減される。同じメモリをカバーするのに必要なエントリ数が大幅に削減され、作業セットがTLBに快適に収まり、TLBミスやページテーブルウォークも大幅に減る。
結果として、StrategyGetBufferがロック保持中にフォルトを引き起こすことがなくなり、回帰が消失する。
PostgreSQLでHuge Pagesを設定するには、huge_pages設定パラメーターを使用する。デフォルトのtryではなくonを推奨し、Huge Pagesが利用できない場合はPostgreSQLの起動を失敗させることで、設定ミスを防げる。
ただし、Huge Pagesにはトレードオフもある。事前に割り当て・予約されるため、PostgreSQLが全て使用していなくても他のシステムでメモリが利用できなくなる。また、Huge Pageは全体として割り当てられるため、メモリ無駄の懸念もある。
クラウド時代のインフラ運用への示唆
この問題は、現代のクラウドインフラ運用における重要な教訓を示している。OSレベルの変更が、アプリケーション層に予期しない影響を与える可能性があることだ。特に、以下の点で注意が必要である:
- 大規模メモリを扱うデータベースワークロードでは、Huge Pagesの設定が必須となる可能性
- カーネルアップデート時の事前検証の重要性。本番環境での適用前に、負荷テストでのパフォーマンス測定が不可欠
- プロファイリングツールによる継続的なパフォーマンス監視の必要性
AWS、Google Cloud、Azureなどの主要クラウドプロバイダーは、すでにこの問題への対応を進めており、Amazon RDSやCloud SQLなどのマネージドデータベースサービスでのHuge Pages自動設定などが検討されている。また、PostgreSQL 17以降では、この種の問題を軽減するためのスピンロック実装の改善も予定されている。
詳細はHow Linux 7.0 Broke PostgreSQL: The Preemption Regression Explainedを参照していただきたい。