抄訳 シリーズブログ Failover and Flexible Replication Topologies in MySQL 5.6

MySQL開発者のSven Sandbergのブログ MySQL Replication Ideas からの記事を翻訳してみました。一部は省略してあるのでぜひ原文も確認してください。

追記:なお、原文にあるGTID_DONEはMySQL 5.6.9からGTID_EXECUTEDに変わっています。

他にもいくつかGTID関連のパラメタもMySQL 5.6 GA前に変わっていますのでご注意ください。
MySQL :: MySQL 5.6 Reference Manual :: 17.1.4.5 Global Transaction ID Options and Variables

Global Transaction Identifiers – why, what, and how / GTID - なぜ、なに そして どう使う

MySQL 5.6では新たにGTIDが導入されました。GTIDには数々の用途が想定されますが、我々がGTIDを導入した最大の目的はシームレスなフェールオーバーのためです。これによって、マスターで障害が起きた際には、スレーブの一台が新しいマスターとなります。手動での操作やサービス停止時間を最小限に抑えてこの切り替えを可能とします。

このページはGTIDに関するブログポストの最初です。ここでは、複数の利用例や、いかにフェールオーバーを簡単に行えるかを見ていきます。

以降のブログポストでは、このパワフルな機能のより高度な使い方や、詳細な実装やメカニズムを解説します。これによって、レプリケーション環境のトラブルシューティングや障害解析、フェールオーバー設計、その他の機能の理解が進めばと思います。また外部ツールがGTIDをいかに利用できるかなどを見ていきます。

Use cases / (レプリケーションの)利用例

ツリー型:
http://1.bp.blogspot.com/-RsWiQG1PP9A/UGZHqZvFNvI/AAAAAAAAACg/ZCaNRp0epGI/s1600/1-tree.png
(略)

What GTIDs are not / GTIDでできないこと

GTIDでできないことを明確にしておきましょう。サーバの障害を検知することはできません。外部のユーティリティが必要です。また、障害が発生した後での新しい構成を決定することもできません。クライアントに別のサーバ(生き残ったスレーブなど)を知らせることもできません。これらはそもそも実装されていません。GTIDそのものはこれらの処理を行うためのものではありません。GTIDは、問題のある状態の判断や処理についてはサーバの外部のユーティリティなどで行われることを想定しています。

この後のブログポストでは、これらの判断をどのように行えるかを紹介します。またmysqlfailoverユーティリティで実際にフェールオーバー処理などを実装しています。

Anatomy of a Global Transaction Identifier / GTID詳解

実際のGTIDの利用例を見てみましょう。最初のレプリケーションの利用例を見てみます。
http://2.bp.blogspot.com/-t__m-4W411g/UGZHupvqrwI/AAAAAAAAAC4/zCqtes1sb50/s1600/2-tree-failover.png
サーバAがクラッシュしたため、BかCのどちらかを新しいマスターにしたいとします。

レプリケーションは非同期のため、BとCは全てのトランザクションのコピーや実行が済んでいない可能性があります。片方が他方よりも先行していることも。例えばBの方がCよりも先行している場合、Bを新しいマスター選びます。そして、Bには存在していてCに適用されてないトランザクションからCのレプリケーションを再開します。

では実際にどう動くか。マスターでトランザクションをコミットすると、2つのコンポーネントからなるIDが生成されます。

  • 前半はサーバを一意に表す server_uuid (MySQL 5.6から導入されたグローバル変数 128-bitのランダムな数値が生成され、2つのサーバが同じUUDを持つ可能性は非常に低い)
  • 後半はコミットされたトランザクションの番号。最初のトランザクションは1、次は2というようにコミットごとに1つずつカウントアップ。列のオプションauto_incrementのようなイメージ

前半は、異なったサーバで実行されたトランザクションが異なったGTIDを持つために、後半は同じサーバで実行された複数のトランザクションが異なったGTIDを持つために。

文字列としては、GTIDは“UUID:N”のように表現されます。
例: 22096C54-FE03-4B44-95E7-BD3C4400AF21:4711

Identifiers are replicated / IDもコピーされる

GTIDはバイナリログにて、それぞれのトランザクションの前に記録されます。
例:
http://2.bp.blogspot.com/-voF9rqzdV0k/UGZHwzIZDeI/AAAAAAAAADg/_9X-eKz9S98/s1600/8-binlog-with-gtids.png

バイナリログの内容は、GTIDを含めてスレーブに転送されます。スレーブはこのGTIDを読み、スレーブでコミットを行っても同じGTIDを維持します。そのため、スレーブでは新たにGTIDを生成することはありません。スレーブとマスターでは、GTID生成の観点からはコミット時の挙動が異なります。(より正確に言うと、スレーブのスレッドとクライアント処理をするスレッドでは挙動が違う) スレーブ内部での詳細なメカニズムは重要なため、次のブログポストにて詳解します。

Global Transaction Identifiers in Action / GTIDの実際の使われ方

実行済みのGTIDは、gtid_donegtid_executedというグローバル変数で確認できます。この変数はMySQL 5.6で追加された参照専用の変数です。gtid_donegtid_executedはそのサーバでコミットされたGTIDの「範囲」を文字列で格納しています。例えば、下記のIDがトランザクションによって生成されたとします。

    0EB3E4DB-4C31-42E6-9F55-EEBBD608511C:1
    0EB3E4DB-4C31-42E6-9F55-EEBBD608511C:2
    4D8B564F-03F4-4975-856A-0E65C3105328:1
    0EB3E4DB-4C31-42E6-9F55-EEBBD608511C:3
    4D8B564F-03F4-4975-856A-0E65C3105328:2

するとgtid_donegtid_executedの値は以下のようになります。

    "0EB3E4DB-4C31-42E6-9F55-EEBBD608511C:1-3,
    4D8B564F-03F4-4975-856A-0E65C3105328:1-2"

その他の例:

mysql> SELECT @@GLOBAL.GTID_EXECUTED;
+------------------------+
| @@GLOBAL.GTID_EXECUTED |
+------------------------+
|                        |
+------------------------+

mysql> CREATE TABLE tbl (a INT);
mysql> SELECT @@GLOBAL.GTID_EXECUTED;
+----------------------------------------+
| @@GLOBAL.GTID_EXECUTED                 |
+----------------------------------------+
| 4D8B564F-03F4-4975-856A-0E65C3105328:1 |
+----------------------------------------+
mysql> INSERT INTO tbl VALUES (1);
mysql> INSERT INTO tbl VALUES (2);
mysql> INSERT INTO tbl VALUES (3);
mysql> SELECT @@GLOBAL.GTID_EXECUTED;
+------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                   |
+------------------------------------------+
| 4D8B564F-03F4-4975-856A-0E65C3105328:1-4 |
+------------------------------------------+

この変数は、スレーブがマスターに追いついているかどうか、追いついていない場合にはどのトランザクションが未実行かを確認するためにも使えます。

master> SELECT @@GLOBAL.GTID_EXECUTED;
+------------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                         |
+------------------------------------------------+
| 4D8B564F-03F4-4975-856A-0E65C3105328:1-1000000 |
+------------------------------------------------+
slave> SELECT @@GLOBAL.GTID_EXECUTED;
+-----------------------------------------------+
| @@GLOBAL.GTID_EXECUTED                        |
+-----------------------------------------------+
| 4D8B564F-03F4-4975-856A-0E65C3105328:1-999999 |
+-----------------------------------------------+

この場合には、マスタで行われたトランザクションの4D8B564F-03F4-4975-856A-0E65C3105328:1000000がスレーブではまだ実行されていないことを表します。必要な場合には、mysqlbinlogコマンドなどを使って、このトランザクションの内容を確認できます。

The Last Ingredient: New Replication Protocol / 新しいレプリケーションプロトコル

ここまでGTIDがどのように生成され、レプリケーション構成の中でどのように展開されるかを見てきました。続いてはフェールオーバーのための新しい重要な要素、改良されたレプリケーションを見ていきます。

プロトコル:従来はスレーブがマスターに接続すると、バイナリログ名とオフセット(バイナリログのポジション)をリクエストし、それ以降全てを転送していました。新しいプロトコルでは、
1. スレーブがマスターに接続すると、スレーブで実行&コミット済みのGTIDの範囲をマスターに送信する
2. マスターは、「それ以外」のトランザクションを全てスレーブに渡す ※スレーブから送られたGTID以降ではなく、スレーブで実行されていないトランザクション全て

:下記が新しいレプリケーションプロトコルの例
http://4.bp.blogspot.com/-xHF6fk3in_c/UGZHxLtuqcI/AAAAAAAAADo/eLObx289fGs/s1600/9-new-protocol.png
ここではバイナリログの内容を個別に表現しています。特定のSQLが1つのトランザクションで実行されていますがここでは重要ではありません。説明をシンプルにしたかっただけで、実際には複数のクエリが1つのトランザクションに含まれることが多いでしょう。またUUIDの数字そのものなども重要ではないので、GTIDは単に省略してid1, id2などとしてあります。
この例では、Cは“id1...id2”をBに送り、Bはid3のtrx3をCに送っています。もっとトランザクション続けばBに以降を転送し続けます。

SQL:以下のSQL文で新しいプロトコルを利用することを設定できます。

CHANGE MASTER TO MASTER_AUTO_POSITION = 1;

重要なポイントは、手動でGTIDを指定する必要は全く無い点です。スレーブは自動的に実行済みのGTIDをマスターに送信します。MASTER_AUTO_POSITION = 1を設定した場合は、MASTER_LOG_FILEまたはMASTER_LOG_POSを設定することはできません。

Failover / フェールオーバー

GTIDを使ってフェールオーバーを行う準備ができました。
ツリー型:最初の例の場合。先ほどの絵にバイナリログの情報を追加しました。
http://1.bp.blogspot.com/-4ey5LjLEFOg/UH5vD2L30dI/AAAAAAAAAFI/PYhRnBzHXw0/s1600/10b-tree-failover-new-protocol-fixed.png
この絵のように、マスターでは3つのトランザクションが実行されていて、Bでは全てが適用済み、Cには1つだけが転適用された状態だします。そしてAで障害が起きたとします。この場合にはBを新しいマスターとし、CをBのスレーブとします。新しいレプリケーションプロトコルを使うと、Cはid1をBに送ると、Bはid2のtrx2(およびそれ以降にBでコミットされたトランザクション)をCに送ります。

Circle topology(略)

Summary / まとめ

シームレスなフェールオーバーを可能とするため、MySQL 5.6にGTIDが導入されました。GTIDはそのトランザクションが初めてコミットされた時に生成され、別のサーバにレプリケートされた場合には同じIDが維持されます。新しいレプリケーションプロトコルは、フェールオーバーをほぼ自動化します。データベース管理者がGTIDを知っておく必要はありません。管理者が行うべきはどれが新しいマスターかをスレーブに知らせることです。監視で重要になってくるのは@@GLOBAL.GTID_DONE @@GLOBAL.GTID_EXECUTEDです。