ガールフレンド(仮)におけるデータベースの変則レプリケーションとその課題
はじめに
株式会社サイバーエージェントのメディア事業の横断SREチームである「サービスリライアビリティグループ」でSREをしている鬼海 雄太です。 主にソーシャルゲームやAmebaブログなどのコミュニティサービスのインフラの設計運用、改善を担当しています。
本記事ではガールフレンド(仮)におけるデータベースの特殊なレプリケーションとその課題についてご紹介します。
3/7(木) 開催のCyberAgent Game Conference 2024で発表する以下のセッション内容の一部となります。
CAGC2024 12年目を迎えた『ガールフレンド(仮)』におけるデータベースの負債解消への道のり
ガールフレンド(仮)とは
ガールフレンド(仮)(以下GFと表記)は2012年10月にサービスを開始したWebブラウザで遊べるソーシャルゲームです。 リリースから12年目を迎え、ソーシャルゲームとしては歴史のあるサービスとなっています。
GFの構成図
まずはGFの構成を簡略化した図でご紹介します。
GFは弊社のプライベートクラウド上のサーバーで稼働しています。 アプリケーションサーバは仮想マシンですが、データベースサーバは物理マシンでMySQLを利用しています。
MySQLはメインDB・イベントDB・ヒストリDBの3系統に別れており、それぞれに複数台の参照用のレプリカサーバが存在しています。 構成としては非常にオーソドックスなものとなっています。
変則レプリケーション
長年運用が続いているサービスは様々な課題を抱えていることがあります。 本記事ではその課題の中から、メインDBでおこなっている特殊なレプリケーションである変則レプリケーションについてご紹介します。
変則レプリケーションとは
MySQLのソースとレプリカでレプリケーションを構成しているが、レプリカサーバごとにレプリケーションされているテーブルが異なる状態です。 この「変則レプリケーション」は正式名称ではなく、私がそう勝手に呼称しています。
正式名称では、「レプリケーションフィルタリングの機能を用いたレプリケーション」となると思いますが、このあとに紹介する話でフィルタリング機能を使用せず同様の構成を実現しており、 ややこしくなるので変則レプリケーションとして呼称させていただきます。
こちらの図のようにソースからレプリケーションしているレプリカA・B・Cがある状態で、 AはXテーブルのみレプリケーション、 BはYテーブルのみレプリケーション、 Cは全テーブルレプリケーション、
という状態を変則レプリケーションと呼んでいます。
レプリケーションの仕組みをおさらい
まず、MySQLのレプリケーションの仕組みを簡単にご紹介します。
図の左側のソースDBで更新クエリが実行されると、SQL実行プロセスはデータの更新とバイナリログへの書き込みを行います。
その後、BinlogDumpスレッドがバイナリログの更新を検知し、レプリカDBにバイナリログイベントを送信します。
右側のレプリカDBでは、まずI/Oスレッドがバイナリログからイベントを受信し、リレーログと呼ばれるファイルに記録します。
そして、レプリカDBのSQLスレッドがリレーログからバイナリログイベントを取り出し、データに対してリプレイすることでレプリケーションが実現しています。
なぜ変則レプリケーションが導入されたのか
10年前の話になるのですが、2013年末から2014年の年始にかけてGFのテレビCMが放送されネット上で大いにバズりました。
その結果、ゲームのユーザー数が短期間で激増しゲーム内イベントの負荷が高騰しました。
具体的にはDBのMySQLへの書き込みクエリ数が大幅に増え、レプリカ側のレプリケーションの処理が追いつかず遅延が深刻化しました。
レプリカ側の遅延によって、ソースとレプリカ間の差分が大きくなるとゲーム進行に支障が出てしまいます。
レプリカDB上のSQLスレッドは更新を並列に処理できず、ソースDBからあまりにも大量の更新クエリが継続して送られてくるとレプリカDB上のデータ更新が追いつかない状態となってしまいます。
現在のMySQLのバージョンでは、レプリカDBのSQLスレッドを並列化できるReplica_Parallel_Workersというオプションがありますが、当時はこの設定はありませんでした。
ソースDBからの更新クエリ量を減らす根本的な解決方法としては、まずデータベース自体を分割して書き込み処理を分散させる方法があります。
しかし、これにはアプリケーション側の大幅な設計変更が必要で、すぐに対応させるのは困難でした。
レプリケーションフィルタリングによる変則レプリケーション
そこでMySQL側だけで解決できる方法として、MySQLのレプリケーションフィルタを使用する方法を採用しました。
レプリケーションフィルタはレプリケートするテーブルを制御し、レプリカDB側の処理量を減らすことが可能です。
レプリカ側のMySQLで以下の設定を有効化します。
- replicate-do-table : レプリケートするテーブルを指定
- replicate-ignore-table : レプリケートを除外するテーブルを指定
この設定を利用し下記の図のような構成をつくりました。
Cardグループにはカード情報関連のテーブルのみレプリケートし、
Giftグループにはギフトボックス関連テーブルのみレプリケート、
Otherグループには、カード情報関連とギフトボックス関連以外のテーブルのみレプリケート、
Allグループはすべてのテーブルをレプリケートするが、アプリケーションからは参照させない。
という構成です。
このフィルタリング構成をとることで、レプリカDB側が送られてきた更新クエリを適用する範囲がグループごとに絞られるので、レプリケーションの遅延を解消することができ、高負荷状態のイベントを乗り切ることができました。
しかし、このフィルター導入によるフィルタリングは以下のデメリットがありました。
- MySQL MHAを利用した冗長化ができない
- サーバー費用が余計にかかる
レプリケーションフィルタリングによる変則レプリケーションではMySQL MHAを利用した冗長化ができない
MySQL MHAは昔からよく利用されている、MySQLを冗長化してくれるサードパーティのツールです。 ソースDBがダウンしたときに、データの不整合を防ぎながらフェイルオーバーを実現できる機能をもっています。
このMySQL MHAはフィルタリング機能を用いた変則レプリケーションの環境では利用することができません。
MySQL MHAではすべてのレプリカDBが同じフィルタリングルールである必要がある為です。