Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Zero-Downtime Database Migrations: Lessons from GitLab's PostgreSQL Architecture

2026年10月16日に「Dev.to」が公開したITニュース「Zero-Downtime Database Migrations: Lessons from GitLab's PostgreSQL Architecture」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

GitLabは、大規模データベースのスキーマ変更時に発生するサービス停止を防ぐため、ロック待機時間の制限、インデックスの並行作成、カラム削除の段階的実施、大量データ更新のバッチ処理化といった技術を導入し、アプリケーションを止めずにシステムを更新する方法を確立している。

ITニュース解説

大規模なシステムを運用していると、データベースの構造を変更する「マイグレーション」は避けて通れない作業となる。しかし、数十億行のデータや数テラバイトもの巨大なテーブル、そして毎秒数千ものトランザクションが処理されるようなシステム(例えばGitLabのようなサービス)では、安易なデータベース変更がシステム全体の停止(ダウンタイム)を引き起こす深刻なリスクとなる。本記事では、GitLabがPostgreSQLデータベースにおいて、いかにしてダウンタイムなしのマイグレーションを実現しているか、そのための重要な原則と技術的なパターンを解説する。

まず、なぜ通常のマイグレーションが問題になるのかを理解する必要がある。PostgreSQLでは、データベースのスキーマ(構造)を変更する際には「ロック」という仕組みが使われる。これは、変更中にデータの一貫性を保つためのもので、特定の操作が行われている間、他の操作を制限する役割を持つ。例えば、新しいカラムを追加するALTER TABLE ... ADD COLUMNのような操作は、AccessExclusiveLockという最も強力なロックを取得する。このロックが取得されている間は、そのテーブルに対する通常の読み込み(SELECT)も書き込み(INSERT, UPDATE, DELETE)もすべてブロックされてしまう。

たとえ、テーブル構造の変更自体が数ミリ秒で完了するとしても、もしその変更が実行される前に、長時間実行されている別の参照クエリ(SELECT)が走っていた場合、ALTER TABLEはロックの解放を待つことになる。この「ロック待ち」が発生すると、ALTER TABLEの後に続く全てのデータベースクエリもまたブロックされてしまい、あっという間にアプリケーションサーバーのデータベース接続が枯渇し、システムが停止してしまう危険性がある。これは、大規模システムにおいて致命的な問題となる。

GitLabはこの問題に対し、いくつかの厳格なルールを設けている。

第一のルールは「常に短いロックタイムアウトを設定する」ことだ。GitLabでは、マイグレーションがロックを無限に待ち続けることを許さない。具体的には、ロックの取得に500ミリ秒以上かかった場合、そのマイグレーションは潔く中断されるよう設定されている。これにより、マイグレーションが他のクエリを長時間ブロックするのを防ぎ、アプリケーション全体のダウンを防ぐ。中断されたマイグレーションは、安全に再試行することが可能であり、ウェブトラフィックに影響を与えることなく処理を進められる。

第二のルールは「インデックスを同期的に作成しない」ことだ。通常のCREATE INDEXコマンドは、インデックスを作成する対象のテーブルに対する書き込み(INSERT, UPDATE, DELETE)を、インデックス作成が完了するまで全面的にブロックしてしまう。もし1億行のテーブルであれば、インデックス作成に30分もかかり、その間ずっとデータベースへの書き込みが停止してしまう事態になりかねない。GitLabでは、CREATE INDEX CONCURRENTLYというPostgreSQLの機能を利用し、インデックスをバックグラウンドで作成する。この方法では、テーブルを二度スキャンしてインデックスを作成するが、その間も通常の読み書き操作はブロックされず、アプリケーションへの影響を最小限に抑えることができる。

第三のルールは「安全なカラム削除のための3段階戦略」だ。データベースからカラムを直接削除する(ALTER TABLE users DROP COLUMN phone;)と、そのカラムを参照している既存のアプリケーションコードが即座にエラーを起こし、クラッシュする可能性がある。GitLabでは、これを避けるために、複数回のリリースにまたがる3段階のプロセスを踏む。まず、最初のリリース(N)で、アプリケーションコード(ORMなど)がそのカラムを参照しないように変更する。これにより、新しいコードはそのカラムを読み書きしなくなる。次のリリース(N+1)では、データベースのスキーマレベルでそのカラムが無視されるように設定し、バックグラウンドジョブなどからも参照を完全に削除する。そして、最後のリリース(N+2)で、ようやく物理的にALTER TABLE users DROP COLUMN phone;を実行する。この段階では、もうどのアプリケーションコードもそのカラムに依存していないため、アプリケーションがクラッシュすることなく安全にカラムを削除できる。

第四のルールは「バッチ処理によるバックグラウンドマイグレーション(BBM)」だ。例えば、5000万行もの大量のデータを更新する必要がある場合(例: 新しいUUIDカラムに値を設定する)、一度にUPDATE users SET uuid = gen_random_uuid();のようなクエリを実行すると、データベースのトランザクションログ(WALログ)が膨大になり、メモリを使い果たし、さらに長時間にわたって大量の行をロックしてしまい、データベース全体のパフォーマンスに甚大な影響を与える。GitLabはこれを解決するために、バッチ処理によるバックグラウンドマイグレーションを使用する。これは、巨大な更新処理を5000行などの小さなバッチに分割し、それを非同期のバックグラウンドワーカー(Sidekiqなど)で少しずつ実行する方法だ。さらに、データベースのレプリケーション遅延(マスターデータベースとレプリカデータベースの間の同期の遅れ)を監視し、遅延が8秒を超えた場合には自動的に処理を一時停止する仕組みも導入されている。これにより、システム全体の負荷を考慮しながら、数時間かけてバックグラウンドで安全に大量データを更新し、ユーザーは全くダウンタイムやクエリの速度低下を感じることなく利用を続けられる。

これらの技術とルールは、GitLabのような極めて大規模で高可用性が求められるシステムにおいて、データベースの構造変更やデータ更新といった重要な運用作業を、ダウンタイムなしで安全に行うために不可欠なものだ。システムエンジニアを目指す上で、このような大規模システムの課題と、それに対する実践的な解決策を知ることは、将来のキャリアにおいて非常に貴重な学びとなるだろう。特に、ロックの理解、非同期処理の活用、そして段階的な変更導入といった概念は、様々なシステムの設計・開発に応用できる重要な知見である。

関連コンテンツ

関連IT用語