アプリケーションの規模がさらに大きくなり、1つのデータセンターに収まりきらなかったり、ディザスタリカバリを考慮する必要が出てくると、「データセンター間でのデータの複製」を考えなければならない。

データセンター間の複製というテーマは、MySQLカンファレンスではFacebookとClickabilityという2社がそれぞれ発表を行った。どちらも、更新は片方のデータセンターに向け、参照を両方のデータセンターから行う形を取った。更新を双方のデータセンターで行なうと、競合検知やフェイルオーバー/フェイルバックなどが複雑になる。これを防ぐために更新を片方に集中するのは、1つの設計パターンとして知られている。参照は双方のデータセンターに対して行うので、性能を上げるためにそれぞれキャッシュサーバを配置する。

Facebookでは、東海岸(サンフランシスコ)と西海岸(バージニア)にデータセンターを持っている。更新はすべて西海岸のデータセンターに対して行う。このとき、以下のような処理を行っている。

  • 西海岸のMySQL(マスター)を更新する
  • 西海岸のmemcachedの当該キャッシュを無効化して新しい値をセット
  • 東海岸のMySQL(スレーブ)が自動的に更新される

このとき問題になるのは、東海岸のmemcached上のキャッシュをどうやって更新するのか、ということである。もし何もしなければ、東海岸のMySQLスレーブが更新されても、東海岸のmemcached上には古いキャッシュが残っているので、古いデータを読んでしまうことになる。常にキャッシュの同期を取るようにすれば大丈夫だが、回線速度の遅さが無視できず、パフォーマンスの問題が起こってくる。

FacebookとClickabilityは、ともにこの問題に直面し、それぞれまったく別のアプローチで解決を行った。

Clickabilityは、最初は「時間差をつけて反対側のデータセンターにキャッシュを無効化通知」というアプローチを取った。しかしMySQLのレプリケーション機能は、遅延時間を一定以内に確実におさえる仕組みがないため、「スレーブにレプリケーション結果が反映されるよりも前にキャッシュが無効化」される危険が残る。こうなると、後続のアプリケーションは古いレコードを読んでキャッシュに入れてしまうので問題になる。そこで、「更新通知専用のテーブル(キューのようなもの)」を別に用意して、キャッシュの無効化通知を、テーブルに無効化したいキャッシュのキーを入れる、という形で行うことにした。テーブルへ投入したキャッシュのキーはMySQLの機能でレプリケーションされる。反対側のデータセンターでは、キューテーブルを監視する常駐ジョブを用意し、キーに対応したキャッシュを無効化するのである。更新は「本体の更新→キューへのキー投入」という順序にすることで、キャッシュの無効化は必ずMySQLのレプリケーションの後に行なわれることになる。

一方Facebookは、MySQLのソースコードを改変するという手段を取った。更新系SQL文に対して、以下のように末尾にmemcached用の指示を埋め込めるようにした。

INSERT INTO xxx VALUES(xxx) MEMCACHE_DIRTY key1, key2, key3...

こうした更新系SQL文はバイナリログに書き込まれ、東海岸のスレーブではリレーログに保存される。リレーログを読むSQLスレッドは、この指示を検知すると、memcachedサーバ上の対象のキーのキャッシュを無効化する。これによって、確実に最新の値がキャッシュに読み込まれるようになるのである。

データセンター間でレプリケーションをするという事例はなかなか少ない。Clickabilityは、データセンター間でのレプリケーション遅延時間が許容範囲内に収まるかどうかを、あらかじめ負荷試験によって確認し、その上で環境を構築したことを紹介していた。レプリケーション遅延は、回線速度も大事だが、更新のトラフィックに引きずられることも多いので、アプリケーションへの依存度が大きい。その意味でも負荷測定は必須である。

Clickabilityは専用テーブルと監視ジョブによって対処し、FacebookはMySQLのソースコードを変更することによって対処した。一般的には、MySQL本体のソースコードを変えるのは、Enterpriseを用いたときにサポートを受けられなくなるだけでなく、バージョンアップが難しくなり、品質面での保証も難しくなるので推奨されない。