GitHubはこの数年間、サイトのリレーショナルデータベースをパーティション化し、データを複数の独立したクラスタに移行する作業を続けている。これにより、負荷を50パーセント削減すると同時に、データベース関連のインシデントを大幅に低減することができた、とGitHubエンジニアのThomas Maurer氏は述べている。
GitHubのアーキテクチャは当初、mysql1
と呼ばれる単一のデータベースクラスタに依存していた。
GitHubの成長に伴って、必然的にこれが問題になりました。データベースを十分なサイズに保つため、より新しい、より大きなマシンへのスケールアップが繰り返されていたのです。
mysql1
に影響するあらゆる種類のインシデントが、このクラスタ上にデータをストアする機能すべてに影響していました。
この状況を改善するために、GitHubのエンジニアたちは、サービスのレベルを損なうことなくリレーショナルデータベースをパーティション化するための戦略を立てた。鍵となるのは、データベーススキーマの仮想パーティションの定義と、SQLリンタ(linter)の導入だった。
仮想パーティションは、クエリやトランザクションでまとめて使用されるテーブルのグループである。スムーズな移行のための第一歩は、アプリケーション層内の分離可能なテーブルグループを特定することだった。テーブル間の関係を体系化するために、スキーマドメインの概念が導入された。以下に示すのは、簡単なYAMLファイルで定義されたスキーマドメインの一例だ。
gists:
— gist_comments
— gists
— starred_gists
repositories:
— issues
— pull_requests
— repositories
users:
— avatars
— gpg_keys
— public_keys
— users
この記述がSQLリンタアプリケーションのベースになる。
[リンタは]クエリアノテーションを付加することで、スキーマドメインを越える違反クエリやトランザクションを識別して、それらを除外します。ドメインが侵害されていなければ、仮想的にパーティション化して、物理的に異なるデータベースクラスタに移動することが可能になります。
リンタはクエリとトランザクションの両方に適用される。クエリリンタは、同じクエリ内で同じスキーマドメインに所属するテーブルのみが参照されていることを検証し、トランザクションクエリは、再検討あるいはデータモデルの更新が必要なトランザクションを特定するために使用される。
スキーマ分析を通じて仮想パーティションが決定されたことで、関連するテーブルを物理的に別のデータベースクラスタに移動することが可能になった。この最終ステップでGitHubにとって最も重要なのは、ダウンタイムを回避することだった。
MySQL上で動作するスケーリング層であるVitessに対する最初の調査の後、GitHubのエンジニアたちは、write-cutoverと称する独自アプローチの実装を行うことにした。このアプローチは、クラスタに新たなレプリカを追加し、レプリケーションを停止するスクリプトを実行することで、レプリカをオリジナルのクラスタから独立させる、というものだ。このアプローチを使ってGitHubは、最も重要な130のテーブルを一気に移行したのだった。
このような努力の結果、GitHubのデータベースをいくつかのクラスタに分散することができた、とMaurer氏は説明する。これにより、処理可能なクエリが30パーセント増加した一方で、各ホストの平均負荷は半分になった。それと同時に、データベース関連のインシデントが大幅に減少したことも確認されている。
Maurer氏の記事には、特にwrite-cutover手順に関して、ここで述べたよりも詳細な説明がある。全体像を理解するためには、氏の記事を読むとよいだろう。