それが僕には楽しかったんです。

ミドルウェアとかやってます

MySQL のレプリケーション実装

はじめに

どうも、年明けおもしろ荘を見ながらブログを書いているけんつです。
前にMySQL 8 と docker-compose を使ってレプリケーションを構築する記事を書いたのだけど、そこではただバイナリログのポジションベースのレプリケーションを構築しただけだったので
この記事でその実装と仕組みを追っていく。

前のレプリケーション構築記事
GTID ベースのレプリケーションには触れていない。
rabbitfoot141.hatenablog.com

レプリケーション実装

レプリケーションはマスターが DB の全ての更新や削除といった変更をバイナリログベースで追跡することがベースとなっている。
このバイナリログはサーバが起動した瞬間からデータ変更以外にもデータベースの構造が変わるなどのイベントを全て記録する。また変更が伴わない SELECT 等はバイナリログ上で追跡されない。

MySQLレプリケーションは、マスタに変更があった場合にスレーブにデータをプッシュするのではなく、マスターからデータをプルするという表現が近い。
また実際に送信されるデータはバイナリログであり、スレーブはこのバイナリログのイベントを再現することで、マスターと同様のデータを再現する。

また各スレーブは独立しているため、スレーブはデータベースのコピーを独自のペースで読み取り、更新できレプリケーションプロセスを他のレプリケーションプロセスに影響を与えることなく開始、停止することが可能となっている。

実装の詳細

MySQLレプリケーションはマスタサーバに1つ、スレーブサーバに2つのスレッドを使用することで実装されている。

サーバ側

  • Binlog dump thread: マスターがスレーブに接続するときにバイナリログの内容をスレーブに送信するスレッドを作る。これがそれ。マスター側で SHOW PROCESSLIST で binlog dumo thread を確認できる。このスレッドはスレーブに送信される各イベントの読み取りの

スレーブ側

  • Slave I/O Thread: START SLAVE がスレーブ側で実行され、マスターに接続した段階でバイナリログの更新記録の送信を要求する I/O スレッドを要求する。このスレッドは Binlog dump Thread が送信するバイナリログの各イベントを読み取るためにマスターのバイナリログでロックを取る。*1
  • Slave SQL Thread: スレーブは Slave I/O Thread によって書き込まれたリレーログを読み取るこのスレッドを作成し、そこに含まれるイベントを実行する。

スレーブは2つのスレッドを使用して、マスターからの更新を読み取ることと、それらを実行することを独立タスクに分類する。
そのため、ステートメント実行が遅い場合でもステートメントを読み取るタスクが遅くなることはない。

SQL スレッドがかなり遅れている場合でも、全てのバイナリログを起動時にフェッチ出来る。またSQL スレッドがフェッチ済みのステートメントの実行を完了する前にスレーブが停止した場合でも安全なコピーがリレーログとしてスレーブローカルに保存されているため次の起動時に実行を開始することができる。これによってバイナリログは送ってさえしまえばマスターで長時間保持する必要がない。

おわりに

MySQL 8 のドキュメント翻訳みたいになってきた。

*1:イベントがスレーブに送信される前でも