読者です 読者をやめる 読者になる 読者になる

(-> % read write unlearn)

All opinions expressed are solely my own and do not express the views or opinions of my employer.

MySQL の Replication 機能の振り返りと MySQL 5.7 での新機能

Oracleさんの「MySQL最新情報セミナー2015春」に参加させて頂いてきました。4時間かけて、MySQLの最新の情報と最新の機能の解説を聞かせて頂きました。MySQL4.1〜5.5で情報のキャッチアップが止まっていREPLICATE_DO_DBる自分としては、新しい情報ばかりで楽しかったです。特にReplicationの機能と MySQ Labs で開発している機能が面白かったです。MySQ Labs では、製品版に対する直接的な機能開発ではなく、もっと先を見据えた実験的な機能の開発とそれに対するコミュニティの意見を得ることを目的としています。RDBの枠を超えたアグレッシブな機能が開発されていて楽しいです。

セミナーの内容はMySQL EnterpriseやMySQL Cluster、Workbenchなど盛り沢山でした。今日は Replication について学んだことを整理・復習してみます。書いてるうちにボリューム増えてしまいました。自分で試したこともないのに書いている機能がたくさんあるので、間違っている所も多々あると思います。教えてください。

MySQLレプリケーションの簡単なおさらい

利点

  • 参照性能のスケールアウト
  • 高可用性構成。マスタ障害時のスレーブのマスタ昇格。
  • 地理的冗長性の実現。
  • バックアップサーバとしての利用。バックアップスレーブのDBを停止してコールドバックアップも可能。

仕組み

  1. マスタは全ての変更点をバイナリログに記録する。実際のクエリの反映はバイナリログに記述した後。
  2. バイナリログの内容をスレーブに転送。スレーブのリレーログに書き込まれる。
  3. スレーブでそのリレーログの内容を順に実行。

  4. スレーブ上のファイル

    • master.info: マスタへの接続情報。読み取り開始位置など。
    • relay-log.info: リレーログをどこまで適用したか。

MySQLレプリケーションの機能追加を時系列で整理

3.23 GA 2001年1月

MySQLレプリケーションは2000年3月リリースの 3.23.15 で追加されました。3.23のGA*1は2001年1月にリリースされました。今年で15年です。

4.0 GA 2003年3月

レプリケーション用のスレッドが、それまでの1スレッド方式からIOスレッドとSQLスレッドの2スレッド方式に変わりました。

  • IOスレッドは、マスタの変更点をスレーブに転送する処理を担います。
  • SQLスレッドは、IOスレッドが転送した変更点をスレーブに反映する処理を担います。

これにより

  • レプリケーションに問題が起きた場合に、通信(IOスレッド)の問題なのかSQLSQLスレッド)の問題なのか分かりやすくなります。
  • スレーブで変更点の反映に時間がかかっていたり、レプリケーションがエラーで止まってしまっていたり、調査や作業のために一時的に変更点の反映を止めている場合にも、「変更点の転送だけは進めておく」ということが可能になります。マスタ上で変更点を記録しているファイルbinlogは、普通は定期的に圧縮か移動か削除を行うので「とりあえず転送だけしておく」ことができるのはメリットです。

4.1 GA 2004年10月

SSLが使用可能に。バイナリログに書き込む時にディスクにフラッシュさせるオプションsync_binlog = 1が導入されました。

5.0 GA 2005年10月

・・・

5.1 GA 2008年11月

Row-based Replication (RBR) が選択可能になりました。それまでの方式は Statement-based Replication (SBR) です。またこの2つの折衷方式の Mixed-based Replication というのもあります。

Statement-based Replication (ステートメントベースレプリケーション

これは、変更点をステートメント(つまりクエリ)として転送します。例えば2000ゴールド持ってるユーザから500ゴールド引くクエリUPDATE users SET gold = gold - 500 WHERE user_id = 1は、そのまま*2クエリの内容が転送され、反映されます。

Row-based Replication(行ベースレプリケーション

これは、変更点を変更結果として転送し、反映します。上記の例で言えば、2000ゴールド持ってるユーザから500ゴールド引いたので、結果として1500ゴールド持っている状態になります。この「結果として1500ゴールド持ってる状態になる」という内容を変更点として転送し、反映します。イメージだけで言えばUPDATE users SET gold = 1500 WHERE user_id = 1という内容が転送され、実行される感じです。(厳密にはかなり違うんだと思います。)

  • 一般的にSBRのほうがログ容量が小さく、そのため高速です。
  • 一般的にRBRのほうが安全です。理由はSBRが非決定性のあるクエリを正確に同期させられないからです。

そうしたら、非決定性のあるクエリは安全にRBR的にレプリして、その他はSBR的に高速にレプリすればいいじゃないですか。それが次です。

Mixed-format logging

便利ですね。そうしたら、もうMixed*3だけあればだいたいOKじゃないかと思いますよね。ただ、myisamではSBRしか使えないので注意します。innodbでは、Mixedが使えます。レプリケーション方式はストレージエンジン依存です。

5.5 GA 2010年12月

準同期レプリケーションがサポートされました。ハートビートがサポートされました。

準同期レプリケーション

これは、スレーブのIOスレッドが変更点を受け取ると「受け取ったよ」という承認をマスタにリプライする方式です。マスタはこの承認を待ってからトランザクションをコミットします。それまでのバージョンでは、以下の3つの処理はそれぞれに独立しており、ゆえには非同期レプリケーションと呼ばれていました。。

  1. マスタ上でのトランザクションのコミット
  2. IOスレッドによる変更点のスレーブへの転送
  3. SQLスレッドによる変更点のスレーブへの反映

準同期レプリケーションでは上記の 1 と 2 の同期を保証します。3 は変わらず非同期です。同期と非同期が混ざっているので準同期と呼びます。

  • トランザクションがコミットされた変更点は必ずスレーブに転送済みであることが保証されるので、準同期レプリは非同期レプリよりデータロストの可能性が減ります。
  • しかし、スレーブの承認を待つ分マスタの更新スループットは落ちます。
    • 漢(オトコ)のコンピュータ道: MySQL 5.5新機能徹底解説に、準同期でスレーブへの転送を保証するのでマスタ上でのディスク同期(sync_binlog)をやめて可用性とパフォーマンスのバランスを取る使い方について書いてありました。なるほど。やったことないけど。

ハートビート

これは、スレーブが定期的にマスタの応答を確認する仕組みです。自分としては、使ったことあるのかすら気にしたことがありません。スレーブは、最初にIOスレッドのコネクションを確立した後は、マスタが変更点を送ってくれるのをただただ受け付けるだけです。基本的にマスタに対して情報をpushやpullしません*4

5.6 GA 2013年2月

レプリケーション構成全体で一意なID(GTID)をクエリに付ける事ができるようになりました。スレーブをクラッシュセーフに設定することが可能になりました。マルチスレッドでSQLスレッドを実行可能になりました。バイナリログにチェックサムが付くようになりました。

Global Transaction IDs (GTID)

これは、レプリケーションのトポロジ全体においてクエリを一意に識別するIDです。サーバのIDとクエリのIDで構成されています。このIDはスレーブ間で比較することができます。

  • そのため、マスタのクラッシュ時に、変更点が最も先まで適用されたスレーブを簡単に探すことができます。そのスレーブはマスタ昇格の候補と言えます。
  • また、あるスレーブがマスタに昇格した際に、「昇格しなかったスレーブ」が「昇格した新マスタ」のbinlogのどの変更点から受け取り始めればいいのかをGTIDで簡単に特定できます。

この2つのメリットを活用した自動フェイルオーバの機能が MySQL Utitlities*5 で利用できるようになりました。。自分は自動フェイルオーバと言ったら『Mobageを支える技術 ~ソーシャルゲームの舞台裏~ 』に出てくる松信さんのMySQL-MHAしか知りませんでした。GTIDについては、「レプリケーションを構成する全てのMySQLでGTIDを使用しないといけない」といった制約や、リリース初期に結構バギーだったらしいことなどから、あまり積極的に導入されていない印象です*6MySQL-MHAのほうが古いし、利用実績も多そうな印象です。実際はどうなんでしょう。

relay_log_info_repository = TABLE

これは、リレーログの情報をファイルではなくテーブルに書き込む設定です。またリレーログをスレーブに反映する際に、それと同じトランザクションでリレーログの情報を書き込んだテーブルも更新します。これにより「リレーログの反映」と「リレーログの反映済みポジションの更新」が同期します。

relay_log_recovery = ON

これは、リレーログに問題が起きたら、再送してもらうという設定です。

relay_log_info_repository = TABLErelay_log_recovery = ON

これを有効にすると、リレーログの反映が安全になり、かつリレーログ自体も堅牢になります。スレーブがクラッシュした際にも、再起動すれば、手動によるポジションの調整をしなくても適切なポジションからレプリケーションを開始できます。この状態を スレーブがクラッシュセーフである と呼びます。

SQLスレッドをマルチスレッドで実行

これをすると、変更点の反映が高速になる可能性があるのでレプリケーション遅延が低減するかもしれません。ただし、5.6では、スキーマ単位での並列実行です。つまり同じスキーマへの変更点の適用は並列化されません。

バイナリログにチェックサム

これを付けると、変更点の転送中にデータが破損しても、スレーブがそれを検知することが可能です。破損を検出するとスレーブはマスタから変更点の内容を自動で再送してもらいます。binlog_checksum = CRC32*7のように設定します。

5.7.7 RC 2015年4月

スレーブが複数のマスタを持てるようになりました。SQLスレッドをマルチスレッド化した場合に、スキーマ内でも変更点の反映を並列化します。レプリケーションする/しないのフィルタ設定をオンラインで変更できるようになりました。オンラインでGTIDモードに移行できるようになりました。準同期レプリケーションが改良され、より堅牢なデータ同期が可能になりました。 5.7はまだGAがリリースされていません。RC*8 とGAとの間では機能の追加/削除はあまり行われない方針です。細かな機能調整やバグFIXが中心です。なのでRCが出た段階で機能キャッチアップしておけばOKです。

Multi-Source Replication (マルチソースレプリケーション

これは、スレーブがマスタを複数指定するレプリケーションです。複数のマスタでの変更点を1台のスレーブに集約するという使い方ができます。準同期レプリやSQLスレッドのマルチスレッド化にも対応しているため、集約しても高速に同期できるかもしれません。用途としてはバックアップサーバを1台に集約する、とかありそうです。変更点の競合を検知したり解消したりすつ仕組みは無いため、データの配置構成の工夫(シャーディングなど)やアプリケーションでの制御が必要になります。CHNAGE MASTERSTART SLAVEなどにCHANNELの指定を追加して設定します。こんな感じです。

CHANGE MASTER TO
  MASTER_HOST = 'mdb01',
  MASTER_USER = 'repli_user',
  FOR CHANNEL "master-01";
START SLAVE FOR CHANNEL "master-01";
STOP SLAVE FOR CHANNEL "master-01";

5.6で追加されたSQLスレッドのマルチスレッド化が、スキーマ内でも効く

ように改良されました。MySQLの起動時に以下のオプションを使用して設定します。

マスタ

--binlog-max-flush-queue-time=0  # デフォルト値も0

スレーブ

--slave-parallel-type=LOGICAL_CLOCK  # デフォルト値は DATABASE
--slave-parallel-workers=3  # デフォルト値は0。2以上の値を設定すればいいのだろうか。それとも1以上の値?

CAHNGE REPLICATION FILTER

このコマンドは、動的にレプリケーションフィルタの設定を変更します。これまで、レプリケーションフィルタの設定はmy.cnfに書いていたので、変更を適用するためには再起動が必要でした*9。例えばこんな感じです。

STOP SLAVE SQL_THREAD;
CHANGE REPLICATION FILTER REPLICATE_DO_DB(foo_db);

フィルタリングルールには以下が含まれます。

  • REPLICATE_DO_DB
  • REPLICATE_IGNORE_DB
  • REPLICATE_DO_TABLE
  • REPLICATE_IGNORE_TABLE
  • REPLICATE_WILD_DO_TABLE
  • REPLICATE_WILD_IGNORE_TABLE
  • REPLICATE_REWRITE_DB*10

オンラインでGTIDモードに変更可能

5.7.6以降のMySQLにアップグレードすれば、MySQLの停止をしなくてもGTIDモードに変更可能です。あれ、でも5.7.6にアップグレードするの時に停止は必要ですよね。それでも便利は便利だと思います。GTIDモードの普及具合が気になります。

オンラインでGTIDモードに変更可能 rpl_semi_sync_master_wait_point = AFTER_SYNC この設定は、スレーブが変更点の内容を受け取った承認が届くのを待ってからトランザクションをコミットします。5.6での準同期レプリケーションは、スレーブが変更点の内容を受け取った承認が届くのを待ってから、クライアントに応答を返します。トランザクションのコミットはそれよりも前に終えています。5.6と同じ方式にする場合はrpl_semi_sync_master_wait_point = AFTER_COMMITを指定します。5.7ではデフォルト値はAFTER_SYNCです。AFTER_SYNCの方式はLoss-Less Replicationとも呼ばれ、マスタ障害が発生してもデータ損失の発生がありません。(なぜなら、変更点がスレーブに渡るまでコミットしないので。)

以上です。長くなってしまいました。間違いもたくさんありそうです。明日は MySQL Labs で開発してる機能について、教えて頂いた内容を整理したいです。あさってはMySQL5.7のレプリケーション以外の新機能について書けたらいいなと思ってますが、平日ですね。

*1:General Availability 安定版

*2:関数によっては、全く完全にそのままというわけではないです。例えば、現在時刻を返すNOW関数をNOW関数そのままに素直に転送して反映するとしたら、レプリケーションに1秒かかる場合にマスタとスレーブで日時の値が1秒ずれてしまいます。そういう関数はMySQLが気を効かせてくれています。しかし、MySQLが気を効かせることができない関数もあるので注意します。例えばランダムな数値を生成するRAND関数です。RAND関数は実行する度に結果が変わりうるからです。このような性質を非決定性と言います。RAND関数を使用しているような非決定性のあるクエリはステートメントベースレプリケーションで正確にデータを同期することができません。また、非決定性は関数以外の表現からも発生します。例えばORDER BYがなかったり中途半端だったりすると行の順序に非決定性が発生します。ここらへん、詳しくはMySQL :: MySQL 5.7 Reference Manual :: 18.2.1.1 Advantages and Disadvantages of Statement-Based and Row-Based Replicationを見ます。

*3:Mixed-format logging は mixed-based replication や mixed-format replication とも呼ばれると書いてあります。しかし、RBRやSBR的なノリでこれらをMBRと呼称する表記は公式リファレンス中に見かけませんでした。なので、ブログ中では略記する際は「Mixed」としています。ちなみに、MBRで公式のリファレンスを検索するとMinimum Bounding Rectangles とか出てきます。これは何なのか知りません。

*4:厳密には違います。例えば準同期レプリケーションでは変更点の受け取りの承認をマスタに返します。

*5:自分はあまり使用したことがないのですが、便利そうですね。Pythonで作られた便利なコマンドラインツール MySQL Utilities | Think IT(シンクイット)

*6:http://www.slideshare.net/Yuryu/mysql56gtid とか

*7:チェックサム方式はCRC32以外にはありません。NONEという設定もありますが文字通りチェックサムを使わないという設定です。

*8:Release Candidate リリース候補

*9:my.cnf意外に起動オプションとして指定する方法もあるようです。いずれにしても設定変更には再起動が必要なのは変わりません。

*10:変更点を適用するスキーマを変えることができる設定。マスタのfooスキーマへの更新をスレーブのdummy_fooスキーマに適用する、とかできます。詳しくはreplicate-rewrite-dbについて - Qiita