2013年9月8日日曜日

MySQL 5.6 の GTID のしくみ

MySQL 5.6 で GTID (Global Transaction ID)が導入されました。どうも使うと便利そうだ、というものの実際どんなものかイマイチ分からなかったので、調べてみました。

サーバーがUUIDを持つ

私は調べるまで誤解していたのですが、UUIDを持つのはサーバーであって、トランザクションではありません。それぞれのサーバーにUUIDが割り当てられ、トランザクションは連番が振られます。
例えば、4台のMySQLインスタンスで1台をマスターにした場合は以下のようににUUIDが振られます。
そして、トランザクションは次のようにIDが振られます。
UUID:トランザクション番号
例えば"40bbcf9b-e556-45b0-bdb3-e528f0041652"で実行された123番目のトランザクションは
40bbcf9b-e556-45b0-bdb3-e528f0041652:123
という感じになります。
そして、それぞれのサーバーで実行されたトランザクションは "GTID_Executed" に "uuid:最初-最後" というように記録されます。
この図の場合では、右端のスレーブのみ最後のトランザクションが実行されず、遅れていることがわかります。
ちなみに、途中のトランザクションだけ抜けた場合は、
40bbcf9b-e556-45b0-bdb3-e528f0041652:1-20:22-1021
という感じで、 : でつなげて記録されます。

マスターを切り替える

ではマスターが落ちた場合の動作を見てみましょう。
マスターが落ちました。右端のスレーブが遅れているので除外し、今回は左端のスレーブをマスターに昇格させます。
この場合、左側のスレーブは新マスターに追いついているので、新マスターの最新のログポジションに合わせれば良いことがわかります。右側のスレーブはひとつ遅れているので、新マスターの該当のログポジションに合わせます。これらが自動計算され CHANGE MASTER TO に MASTER_AUTO_POSITION=1 を指定するだけで良いのが GTID の売りです。

新マスターでのトランザクション

新マスターでトランザクションが実行された場合は、新マスターのUUID:1 から新しくトランザクションが数えられます。
GTID_Executed にはこれまで実行されたトランザクションがすべて記録されます。マスターが変更された場合は複数行記録されます。

新しくスレーブを追加

旧マスターに対するスナップショットから新しくスレーブを追加することもできます
右端に新しくスレーブを追加しました。旧マスターのトランザクションのみ実行され、新マスターのトランザクションが実行されていないことが GTID_Executed からわかりますので、新マスターに接続した時にポジションを新マスターの場所に自動的に設定します。

バイナリログの中身

GTIDを有効にした状態では、バイナリログに
SET @@SESSION.GTID_NEXT= 'a8679745-1864-11e3-a1ec-28924a2bea1c:14'
という行が現れます。これは、次に実行するトランザクションのIDを指定しています。
GTID_NEXT は通常であれば "AUTOMATIC" に設定されていて、その状態では "自分のuuid:次の番号" のIDを次のトランザクションに設定しますが、設定し直すと任意のIDのトランザクションを作成することができます。
# at 2621
#130908 21:05:04 server id 10  end_log_pos 2669 CRC32 0x20b2bc89        GTID [commit=yes]
SET @@SESSION.GTID_NEXT= 'a8679745-1864-11e3-a1ec-28924a2bea1c:14'/*!*/;
# at 2669
#130908 21:05:04 server id 10  end_log_pos 2737 CRC32 0x5e5fa3bb        Query   thread_id=10    exec_time=0     error_code=0
SET TIMESTAMP=1378641904/*!*/;
BEGIN
/*!*/;
# at 2737
#130908 21:05:04 server id 10  end_log_pos 2785 CRC32 0x757ca2c9        Table_map: `yuryu`.`test` mapped to number 70
# at 2785
#130908 21:05:04 server id 10  end_log_pos 2829 CRC32 0x4e043fe7        Write_rows: table id 70 flags: STMT_END_F

BINLOG '
8GcsUhMKAAAAMAAAAOEKAAAAAEYAAAAAAAEABXl1cnl1AAR0ZXN0AAEIAADJonx1
8GcsUh4KAAAALAAAAA0LAAAAAEYAAAAAAAEAAgAB//4RAAAAAAAAAOc/BE4=
'/*!*/;
# at 2829
#130908 21:05:04 server id 10  end_log_pos 2860 CRC32 0x3fbac367        Xid = 226
COMMIT/*!*/;
つまり、バイナリログがスレーブに転送されて実行されるのは
  1. GTID_NEXT を次に実行されるトランザクションID(マスターと同じもの)にセット
  2. BEGIN ~ COMMIT でトランザクションを実行
ということになります。

まとめ

  • サーバーごとにUUIDを付与
  • トランザクションIDは サーバーUUID:番号 として連番で生成
  • スレーブで実行するときは GTID_NEXT を指定して同じトランザクションを再生
  • 実行したトランザクションは GTID_EXECUTED に記録される
以上のような仕組みで GTID は自分がどこまでのトランザクションを実行したのか追跡しています。

参考資料

PerconaのMySQL 5.6 GTID in a nutshellというスライドが大変参考になりました。

2 件のコメント: