このブログ記事では、MySQL(および Percona Server)環境におけるレプリケーション手法に関して改めて考察を行いたいと思います。あわせて、時折見受けられるレプリケーションの間違った考え方についても考えてみます。
私がソリューションエンジニアとして働き始めてからというもの、沢山の情報が公開されているにも関わらず、レプリケーションに対する「誤解」や「不完全な理解」が無くならないことを日々感じていました。
そもそもレプリケーションとは何か?
レプリケーションは、1つのデータベースにデータを蓄積するだけでなく、別のデータベースにデータを複製し、転送することを保証してくれる機能です (複製・転送は、元となる環境で実行されるトランザクション単位で行われます)。
この機能は、参照クエリーの実行や、管理・運用処理のためのセカンダリサーバを既存のインフラ環境に用意するために使用されます。以下の図は、MySQLレプリケーション環境の一例です。
どのレプリケーション手法を選べばよいのか?
レプリケーション手法を決める場合、いくつかの選択肢が存在します。
1) スタンダードな非同期レプリケーション
非同期レプリケーションとは、マスターでトランザクション(更新処理)が完了したとしても、スレーブにはその時点で何も影響を与えていないということです。
変更が完了した後、マスターはバイナリログにデータの変更内容、もしくは更新クエリーそのものを書き込みます(前者はROWベース、後者はSTATEMENTベースのレプリケーションです)。 マスター上のdumpスレッドはバイナリログの内容を読み取り、スレーブのIOスレッドに送信します。そして、スレーブのIOスレッドはその内容を「処理を貯めるキュー」(リレーログ)に書き込みます。
その後、スレーブはリレーログに書かれた変更をSQLスレッドを使って実行していきます(※1)。
※ 訳注1:ここでようやくマスターとスレーブ間で同期が取れます
2) 準同期レプリケーション
準同期レプリケーションとは、スレーブとマスターが、トランザクションを正常に受け取ったことを相互に確認しながら行われるレプリケーション方式です。マスターは、スレーブから「リレーログにイベントを正常に書き込みました」という応答を受け取って初めて、次の更新処理を行うことができます(※2)。
※ 訳注2:つまり、スレーブから応答がない限り、先に進めなくなります
なお、準同期レプリケーションは、トランザクションがスレーブに正しくコピーされたことを保証してくれますが、正常にコミットされたかどうかまでは保証してくれません。
また、準同期レプリケーション環境下では、少なくとも1台のスレーブからACKが返ってくるまで(もしくはタイムアウトするまで)、マスターが次のトランザクションに進まず待ち続けることは、注意すべきポイントです。準同期レプリケーションは、非同期レプリケーションに比べ、より整合性が担保されていると言えます。
最後に、準同期レプリケーションではスレーブのACKを待つという性質上、パフォーマンスに影響を与えてしまうことは忘れないで下さい。
3) グループレプリケーション
グループレプリケーションは、MySQL5.7から導入された新しいレプリケーション方式で、MySQL5.7.17でGAとなりました。 一言でいうなら、「仮想同期レプリケーション」を実現する全く新しいプラグイン(追加機能)です。
グループ内のノードにトランザクションを発行すると、その内容を他ノードに伝播(コミットできるかの確認)し、それで問題がなければ発行元のクライアントに対して完了(コミット)を返します。その挙動は、従来のレプリケーションの方式とは全く異なりますが、グループレプリケーションの機能はこれまで同様バイナリログに基づいています。
以下は、グループレプリケーションのアーキテクチャの一例です。
もしグループレプリケーションに興味があれば、以下のブログ記事をご覧になってください。
http://mysqlhighavailability.com/mysql-group-replication-its-in-5-7-17-ga/ http://mysqlhighavailability.com/performance-evaluation-mysql-5-7-group-replication/
また、2017年4月に開催される「 Percona Live Open Source Database Conference」というイベントで、グループレプリケーションのチュートリアルセッションを行いますので、お楽しみに!
4) Percona XtraDB Cluster / Galera Cluster
もしグループレプリケーションのような機能が必要な場合は、Percona XtraDB Cluster(PXC)も選択肢の一つです。 このソリューションは、ノード間の整合性を保ちながら全ノードの同期を取ることを重視しており、トランザクションの競合や不正な実行を避けるための「certification」という機能が実装されています。
これは即ちクラスタリングソリューションです。クラスタ内の各ノードには全く同じデータが入っており、ノード間では整合性が担保されているのです。
Percona XtraDB Cluster は、複数のコンポーネントから構成されています。
Percona Server for MySQL
Percona XtraDB Cluster(ノードの追加 or 復旧時に稼働中の他ノードからデータを取得するため)
wsrep pathes / Galera Library
Percona XtraDB Cluster も、グループレプリケーションと同じく「仮想同期」を実現します。それに加えて、マルチマスターのレプリケーション方式としても利用可能です。Percona XtraDB Clusterのようなソリューションは、あなたのDB環境の可用性を必ずや高めてくれるでしょう。
Percona XtraDB Clusterに関しても、2017年4月の「 Percona XtraDB Cluster」で、チュートリアルセッションを行います!
ROWベースレプリケーション vs STATEMENTベースレプリケーション
STATEMENTベースレプリケーションでは、SQL(更新クエリー)がそのままバイナリログに書かれます。例えば、マスターで実行された INSERT/UPDATE/DELETE クエリーと全く同じクエリーがスレーブで実行されるのです。
STATEMENTベースのメリット・デメリットは以下の通りです。
【メリット】
クエリーがそのままバイナリログに記録されるので、データベースの「監査」が非常に楽になります
マスター・スレーブ間のネットワーク上でやり取りされるデータ量は少なくなります(SQL文だけなので)
【デメリット】
「非決定性」の更新クエリー(※3)が実行されると、マスター・スレーブ間で不整合が発生する可能性があります
一部のクエリー(INSERT ... SELECT 等)がレプリケーションされた時、パフォーマンスが落ちてしまうかもしれません
オプティマイズや実行のために時間がかかり、遅くなってしまうこともあります
※ 訳注3:RAND()関数、hostname変数など、実行するタイミングや環境によって実行結果が変わってしまうクエリー。詳細は下記URLを参照してください。 https://dev.mysql.com/doc/refman/5.6/ja/replication-rbr-safe-unsafe.html
ROWベースのレプリケーションは、MySQL 5.7.7以降のデフォルトとなり、多くの利点があります。基本的に「レコードの変更内容」のみがバイナリログに記録され、SQLなどの文字情報は記録されません。そのため、STATEMENTベースで問題になる「非決定性」の更新クエリーであっても、マスター・スレーブ間で不整合が発生しません。
ROWベースのメリット・デメリットは以下の通りです。
【メリット】
少ない行を更新するクエリーが多並列で実行されるワークロードであれば、パフォーマンスが向上します
マスターとスレーブ間のデータ整合性が劇的に上がります
【デメリット】
多くの行を更新するクエリーが実行された場合、スレーブに送られるデータ量が急増するため、ネットワーク負荷は高くなります
データベースへの変更に関する「監査」は困難になります
場合によっては、STATEMENTベースよりもパフォーマンスが落ちてしまうこともあります
レプリケーションに対する誤解
レプリケーションは「クラスタ」だ!
MySQLの標準的な非同期レプリケーションは、「同期されたクラスタ」とは異なるものです。非同期(準同期)レプリケーションは、マスターとスレーブ間で常に同じデータになっていることを保証してはいない、という点を忘れないでください。
ただし、Percona XtraDB Cluster の場合は上記と異なり、全ノードで常に同期が取れていることを保証する仕組みになっています。もし、同期が取れないノードが存在した場合は、クラスタから切り離されます。
非同期レプリケーションは完全な「フェイルセーフ」ではありません。データの不整合が生じていても、レコードがSELECTできてしまいます。
レプリケーションは完璧な機能のようだし、障害発生時にもDB稼働を継続可能な「フェイルオーバーソリューション」が実現できる
理論上は、たしかにできるかもしれません。しかしながら、MySQLにはデータの整合性やパフォーマンスに影響を与える多くのパラメータ変数が存在し、それらを十分考慮する必要があるでしょう。また、非同期レプリケーションを使う限りは、トランザクションがスレーブに正しく伝播された保証はどこにもありません。
もし、データ永続性に関わる設定を完璧に考慮し、不整合の問題を完全に回避したとしても、今後はパフォーマンスが落ちてしまう問題に直面するでしょう。
ちなみに、マスターとスレーブ間の整合性をチェックしたいときは、pt-table-checksum
(Percona Toolkitに含まれるツールの一つ)を利用すると良いでしょう。
レプリケーションを構築しているから、バックアップは取らなくていいよね
たしかにレプリケーションはデータを複製してくれる素晴らしい機能であり、スレーブを利用して監視やSELECTの分散、バックアップ作成などが実現できます。しかしながら、レプリケーションはバックアップのためのソリューションではありません。
自然災害などでマスター・スレーブが全てダウンしたり、DB管理者のオペミス(誤ってマスターでDROP DATABASEを実行等)した、およびその他のケース(ex.『Bobby Tables comic』※4)では、バックアップとしての役割を果たせなくなります。そのため、オフサイトバックアップ(mysqldumpでdumpファイルを取得し、別環境に退避する等)を用意して、いつでも環境を再構築できるようにすることを推奨します。
また、中には上記の問題を解決するために遅延レプリケーションを導入するユーザもいます。それでも、Disaster Recoveryとしては有効な手立てにはならないでしょう。
※ 訳注4:SQLインジェクションによって、悪意のある攻撃者にDROP TABLE文が実行され、データが全て消失してしまったケースを描いています
レプリケーション環境で、更新クエリー(トランザクション)をロードバランサで分散させようと思います
2台目のDBサーバ(スレーブ)を用意したことで、システムの可用性を高めることはできたかもしれませんが、参照クエリーはスレーブ、更新クエリーはマスターというように、指定する必要があります。
そのためには、プロキシツールを利用するか、アプリケーション内に上記のような機能を組み込む必要があります。
レプリケーションを導入したら、マスターの性能が急激に落ちるんでしょう?
レプリケーションがマスターに与えるパフォーマンス面の影響度は大きくありません。Peterがこの問題に関して興味深いブログ記事を書いているので、紹介したいと思います。
重要なポイントは、バイナリログへの書き込み処理がパフォーマンスに影響を与えてしまう可能性がある点です。特に、細かなトランザクションが多数実行され、なおかつスレーブ台数が多いような環境では、パフォーマンスの低下が大きくなるかもしれません。
もちろん、その他のパラメータ設定によっては、パフォーマンスに影響が出てしまう可能性もあるでしょう。