MySQL Semi-synchronous Replication:Group Replicationなしでゼロデータロスを実現する設定方法

MySQL tutorial - IT technology blog
MySQL tutorial - IT technology blog

MySQLのデフォルト非同期レプリケーションには致命的な弱点がある。masterがcommitを完了した時点で「終わり」として、slaveがデータを受け取ったかどうかを気にしない。もしその瞬間にmasterがクラッシュすれば、データが失われる。

まさにこれを午前3時に経験した。サーバーがクラッシュし、slaveのラグが15秒あり、その15秒分のトランザクションが全て消えた。それ以来別の方法を探し始め、午前3時にデータベースの破損が発生してバックアップからリストアする羽目になった。その経験から毎日バックアップを確認し、トランザクション系のデータベースには純粋な非同期レプリケーションを使わないようにしている。

Semi-synchronous Replicationはまさにこの問題を解決するために生まれた。MySQL 5.5から標準で搭載されており、追加ソフトウェアのインストールは一切不要だ。

レプリケーションの3つのアプローチ — どれが最適か?

設定に入る前に、3つのアプローチの違いとそれぞれのトレードオフを正確に理解しておく必要がある。

1. 非同期レプリケーション(デフォルト)

masterがcommit → 即座にクライアントへ結果を返す。slaveは「都合のよいときに」binlogを受け取る。これは通常のmaster-slaveをセットアップした際のデフォルト設定だ。

  • 速度:最速。slaveによってmasterがブロックされない
  • リスク:slaveがbinlogを受け取る前にmasterがクラッシュするとデータが失われる
  • 適用場面:純粋なread replica、レポーティング用slave、数秒のデータロスが許容できるアプリケーション

2. Semi-synchronous Replication

masterがcommit → 少なくとも1つのslaveがbinlogをrelay logに受け取ったことを確認するまで待機 → その後クライアントへ結果を返す。slaveはクエリを適用し終える必要はなく、relay logへの書き込みだけで十分だ。

  • 速度:非同期より若干遅い。ネットワークの往復が1回増えるため、LAN上では通常1〜5ms程度の遅延が加わる
  • 安全性:ゼロデータロス。commitが成功する前に、データが少なくとも2か所に存在していることが保証される
  • 適用場面:高い信頼性が求められるプロダクション環境。複雑なGroup Replicationのセットアップを避けたい場合

3. Group Replication / InnoDB Cluster

マルチmaster、自動フェイルオーバー、Paxosコンセンサスプロトコルを採用。最も強力なソリューションだが、運用面でも最も複雑だ。

  • 速度:ノード数とネットワーク遅延に依存し、通常はsemi-syncより遅い
  • 安全性:最高水準。スプリットブレインとフェイルオーバーを自動処理
  • 適用場面:完全自動のHA構成が必要で、分散システムの運用経験を持つチーム向け

Semi-syncのメリットとデメリット分析

実際のプロダクション環境で3つ全てを試した結果、各ソリューションの真のバランスポイントは以下の通りだ。

  • データロスリスク:非同期 = あり | Semi-sync = ほぼなし | Group Replication = なし
  • 書き込みレイテンシの増加:非同期 = 0ms | Semi-sync = +1〜5ms(LAN)| Group Replication = +5〜20ms
  • セットアップの複雑さ:非同期 = 低 | Semi-sync = 低 | Group Replication = 高
  • 自動フェイルオーバー:非同期 = なし | Semi-sync = なし | Group Replication = あり
  • 最小ノード数:非同期 = 2 | Semi-sync = 2 | Group Replication = 3(奇数)

実際の結論:Semi-syncはほとんどのプロダクションケースにおける最良のバランスポイントだ。最小限のセットアップ作業でゼロデータロスを実現できる。チームがGroup Replicationの運用に備えていなければ、Semi-syncが正しい選択となる。

Semi-syncを使うべき時(と使わない方がよい時)

以下の条件のうち少なくとも一つに当てはまる場合、Semi-syncが適している。

  • 金融取引や注文データを管理するデータベース — データロスは絶対に許されない
  • 既存のMaster-Slave非同期レプリケーション環境を、ゼロから再構築せずにアップグレードしたい
  • masterとslaveのネットワーク遅延が10ms未満(同一データセンターまたは同一AZ)
  • チームにGroup Replicationの運用経験がない

使わない方がよい場合:masterとslaveが異なるデータセンターにあり遅延が高い場合(各書き込みがその遅延分だけ遅くなる)、または数msの遅延増加も許容できないほどwrite throughputが極めて高いアプリケーション(数千TPS)。

MySQL Semi-synchronous Replicationの設定手順

開始前の前提条件

  • MySQL 5.7以上(MySQL 8.0を推奨)
  • Master-Slave非同期レプリケーションが安定稼働している
  • InnoDBストレージエンジン(semi-syncでは必須)
  • masterとslaveの間で安定したネットワーク接続と低遅延が確保されている

基本的なレプリケーションがまだない場合は、「MySQL Master-Slaveレプリケーションの設定」を先に読んでから戻ってきてほしい。Semi-syncは既存の設定に追加する一層に過ぎない。

手順1:Master側でSemi-syncプラグインを有効化する

Semi-syncはMySQLのビルトインプラグインを使用するため、外部パッケージの追加インストールは不要だ。

-- MySQLのmasterに接続する
-- masterにsemi-syncプラグインをインストール
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';

-- semi-syncを有効化
SET GLOBAL rpl_semi_sync_master_enabled = 1;

-- slaveが応答しない場合にasyncにフォールバックするまでのタイムアウト(ms)
-- 10000ms = 10秒。要件に合わせて調整すること
SET GLOBAL rpl_semi_sync_master_timeout = 10000;

-- masterがcommitする前にACKが必要なslaveの最小数(デフォルトは1)
SET GLOBAL rpl_semi_sync_master_wait_for_slave_count = 1;

-- 設定した内容を確認
SHOW VARIABLES LIKE 'rpl_semi_sync%';

再起動後も設定を維持するため、master側の /etc/mysql/mysql.conf.d/mysqld.cnf に以下を追加する:

[mysqld]
plugin-load-add = semisync_master.so
rpl_semi_sync_master_enabled = 1
rpl_semi_sync_master_timeout = 10000
rpl_semi_sync_master_wait_for_slave_count = 1

手順2:Slave側でSemi-syncプラグインを有効化する

各slaveノードで同様の手順を実施する:

-- MySQLのslaveに接続する
-- slaveにsemi-syncプラグインをインストール
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';

-- semi-sync slaveを有効化
SET GLOBAL rpl_semi_sync_slave_enabled = 1;

-- semi-syncモードでmasterに再接続するためIOスレッドを再起動
STOP SLAVE IO_THREAD;
START SLAVE IO_THREAD;

slave側の mysqld.cnf に以下を追加して設定を永続化する:

[mysqld]
plugin-load-add = semisync_slave.so
rpl_semi_sync_slave_enabled = 1

MySQL 8.0.26以降のご注意:プラグイン名がmaster/slaveからsource/replicaに変更された:

-- MySQL 8.0.26以降の場合
-- Masterノード
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;

-- Slaveノード
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;

手順3:Semi-syncが正常に動作しているか確認する

-- MASTERで実行
SHOW STATUS LIKE 'Rpl_semi_sync%';

-- 注目すべき重要な値:
-- Rpl_semi_sync_master_status  = ON   -- semi-syncが有効
-- Rpl_semi_sync_master_clients = 1    -- semi-syncで接続中のslaveが1台
-- Rpl_semi_sync_master_yes_tx  = 100  -- slaveからのACKを受けてcommitしたトランザクション数
-- Rpl_semi_sync_master_no_tx   = 0    -- asyncにフォールバックしたトランザクション数(0が望ましい)
-- SLAVEで実行
SHOW STATUS LIKE 'Rpl_semi_sync_slave_status';

-- 期待される出力:
-- Rpl_semi_sync_slave_status = ON

Rpl_semi_sync_master_clients = 0 の場合、semi-syncを有効化した後にslaveが再接続されていないことを意味する。slave側で STOP SLAVE IO_THREAD; START SLAVE IO_THREAD; を再実行してほしい。

手順4:実際にテストする — slaveをオフラインに見立てた検証

semi-syncが期待通りに動作していることを確認するためのテスト:

# ターミナル1:slave側 — IOスレッドを停止してslaveが切断された状態を再現する
mysql -u root -p -e "STOP SLAVE IO_THREAD;"

# ターミナル2:master側 — INSERTを試みてレスポンス時間を計測する
time mysql -u root -p -e "
  INSERT INTO testdb.orders (user_id, amount, created_at)
  VALUES (1, 99.99, NOW());
"
# このコマンドは設定したタイムアウトの約10秒間ブロックされた後に成功する
# タイムアウト後、masterは自動的にasyncにフォールバックする

# ターミナル1:slaveのIOスレッドを再起動する
mysql -u root -p -e "START SLAVE IO_THREAD;"

# master側でasyncにフォールバックしたトランザクション数を確認する
mysql -u root -p -e "SHOW STATUS LIKE 'Rpl_semi_sync_master_no_tx';"
# 値が1増加する — これが先ほどasyncモードで実行されたトランザクションだ

Semi-syncのフォールバックの仕組みとモニタリング方法

slaveがタイムアウト(rpl_semi_sync_master_timeout)内に応答しない場合、masterはアプリケーションのブロックを防ぐために自動的にasyncにフォールバックする。これは意図的な動作であり、slaveの問題によってプロダクション環境が完全に停止するのを防ぐためだ。

しかし、そのフォールバック中はシステムがasyncモードで動作しており、データロスのリスクが再び発生する。このステータスを継続的に監視することが不可欠だ:

#!/bin/bash
# semi-syncステータス監視スクリプト — cronで毎分実行
MYSQL_CMD="mysql -u monitor_user -pYOUR_PASSWORD -N -e"

STATUS=$($MYSQL_CMD "SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Rpl_semi_sync_master_status';")
NO_TX=$($MYSQL_CMD "SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='Rpl_semi_sync_master_no_tx';")

if [ "$STATUS" != "ON" ]; then
  echo "ALERT: Semi-sync OFF — asyncモードで稼働中!"
  # ここにSlack/Telegram/PagerDutyへのアラート送信コマンドを追加する
fi

if [ "$NO_TX" -gt "0" ]; then
  echo "WARNING: $NO_TX 件のトランザクションがasyncにフォールバックしました"
fi

crontabに追加する:

* * * * * /opt/scripts/check_semisync.sh >> /var/log/semisync_monitor.log 2>&1

まとめ

Semi-synchronous Replicationはデフォルトの非同期レプリケーションからのシンプルなアップグレードであり、インフラの再構築やGroup Replicationの習得なしにゼロデータロスを実現できる。既存のMaster-Slave環境があれば、10分以内に有効化できる。

毎日監視すべき2つの指標がある。Rpl_semi_sync_master_statusON であること、そして Rpl_semi_sync_master_no_tx が常に0であること。これらの値に異常が生じた場合、システムはより安全性の低いモードで動作しているため、即座に調査が必要だ。午前3時に発見するような事態は避けてほしい。

Share: