MySQL GTIDレプリケーション設定ガイド:高度なデータ同期と自動フェイルオーバー

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

従来のレプリケーションの問題点 — GTIDで何が解決できるか?

以前からbinlog_file + binlog_posに依存した従来のMySQL Master-Slave構成を設定したことがある方なら、SlaveをMasterに昇格させる際の緊張感はよくご存知でしょう。正確なbinlogの位置を探し出す必要があり、少しでもずれるとレプリケーションがおかしくなり、データが知らないうちに非同期になってしまいます。

筆者は以前、午前3時にデータベースの破損に遭遇し、バックアップからのリストアに約2時間かかった経験があります。その後、システム全体をGTIDに移行しました。GTIDでは各トランザクションにグローバルな一意識別子が付与されるため、MySQLがどのトランザクションが実行済みでどれがまだかを自動的に把握します。ファイル名やオフセットを覚えておく必要がありません。

GTID(Global Transaction Identifier)のフォーマットはsource_id:transaction_idで、例えば3E11FA47-71CA-11E1-9E33-C80AA9429562:23のようになります。フェイルオーバー時、新しいSlaveは新しいMasterに接続するだけで、MySQLが不足分を自動計算して同期を継続します。手動介入は不要です。

環境の準備

この記事ではUbuntu 22.04上で動作するMySQL 8.0サーバー2台を使用します:

  • Master192.168.1.10
  • Slave192.168.1.11

両サーバーにMySQL 8.0をインストールします:

sudo apt update
sudo apt install -y mysql-server-8.0
sudo systemctl enable --now mysql

バージョンを確認します:

mysql --version
# mysql  Ver 8.0.36-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu))

GTIDレプリケーションの詳細設定

ステップ1:Masterの設定(192.168.1.10)

/etc/mysql/mysql.conf.d/mysqld.cnfを編集します:

[mysqld]
server-id                = 1
bind-address             = 0.0.0.0
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW

# GTIDの設定
gtid_mode                = ON
enforce_gtid_consistency = ON

# GTIDで推奨される設定
log_slave_updates        = ON
binlog_expire_logs_seconds = 604800   # binlogを7日間保持

MySQLを再起動します:

sudo systemctl restart mysql

レプリケーション専用ユーザーを作成します — rootは使用しないでください。SlaveのIPに対して権限を適切に制限します:

CREATE USER 'replicator'@'192.168.1.11' IDENTIFIED WITH mysql_native_password BY 'StrongPass!2024';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'192.168.1.11';
FLUSH PRIVILEGES;

Masterからスナップショットをダンプします。--single-transactionを使用してテーブルロックを回避します — DB全体をロックするとアプリケーションが停止する可能性があるため、本番環境では重要なオプションです:

mysqldump \
  --single-transaction \
  --master-data=2 \
  --set-gtid-purged=ON \
  --all-databases \
  -u root -p \
  > /tmp/master_dump.sql

ダンプファイルをSlaveにコピーします:

scp /tmp/master_dump.sql [email protected]:/tmp/

ステップ2:Slaveの設定(192.168.1.11)

Masterと同様ですが、誤ってSlaveに書き込むのを防ぐためread_onlyを追加します — 開発者が誤ったサーバーに接続した際によく発生するミスです:

[mysqld]
server-id                = 2
bind-address             = 0.0.0.0
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW

# GTIDの設定
gtid_mode                = ON
enforce_gtid_consistency = ON
log_slave_updates        = ON

# Slave専用:誤った書き込みを防止
read_only                = ON
super_read_only          = ON
sudo systemctl restart mysql

Masterからのダンプをインポートします:

mysql -u root -p < /tmp/master_dump.sql

SlaveをMasterに接続します。SOURCE_AUTO_POSITION = 1に注目してください — これが従来のレプリケーションとの違いで、ファイルやオフセットを指定する必要がありません:

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST      = '192.168.1.10',
  SOURCE_PORT      = 3306,
  SOURCE_USER      = 'replicator',
  SOURCE_PASSWORD  = 'StrongPass!2024',
  SOURCE_AUTO_POSITION = 1;

START REPLICA;

同期確認とモニタリング

Slaveの状態確認

SHOW REPLICA STATUS\G

確認すべき3つの項目:

Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0

Seconds_Behind_Sourceが0に収束せず増加し続ける場合、Slaveが遅延しています。両サーバーのCPU・ディスク負荷を確認してください。

MasterとSlave間のGTIDを比較して完全な同期を確認します:

-- Master側で実行
SHOW MASTER STATUS\G
-- Executed_Gtid_Set: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-150

-- Slave側で実行
SELECT @@GLOBAL.gtid_executed;
-- 結果はMasterと一致している必要があります

クイックテスト:Masterに書き込み、Slaveで読み取り

-- Master側で実行
CREATE DATABASE gtid_test;
USE gtid_test;
CREATE TABLE ping (id INT AUTO_INCREMENT PRIMARY KEY, ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
INSERT INTO ping VALUES ();

-- Slave側で実行(数秒後)
USE gtid_test;
SELECT * FROM ping;
-- 挿入したレコードが表示されるはずです

レプリケーション遅延監視スクリプト

このスクリプトをcrontabに登録して5分ごとに実行します。閾値の30秒はほとんどのシステムに適していますが、SLAがより厳しい場合は10秒に下げてください:

#!/bin/bash
# /usr/local/bin/check_replica_lag.sh

LAG=$(mysql -u root -p'password' -e "SHOW REPLICA STATUS\G" 2>/dev/null \
      | grep Seconds_Behind_Source \
      | awk '{print $2}')

if [ "$LAG" = "NULL" ]; then
  echo "CRITICAL: レプリケーションが停止しています!"
  # アラートを送信(メール、Telegram、PagerDutyなど)
elif [ "$LAG" -gt 30 ]; then
  echo "WARNING: レプリケーション遅延 = ${LAG}秒"
fi

GTIDによるフェイルオーバー処理

シナリオ:Masterが突然ダウンし、従来のレプリケーションのような数十分ではなく数分以内にSlaveを新しいMasterに昇格させる必要があります。

-- 昇格させるSlave側で実行
STOP REPLICA;
RESET REPLICA ALL;

-- 読み取り専用モードを解除
SET GLOBAL read_only = OFF;
SET GLOBAL super_read_only = OFF;

アプリケーションの接続先を192.168.1.11に変更します。runbookを事前に準備しておけば、通常5分以内に完了します。

昇格したMasterに新しいSlave(192.168.1.12)を追加する場合も、構文はまったく同じです。残りはGTIDが処理します:

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST          = '192.168.1.11',
  SOURCE_USER          = 'replicator',
  SOURCE_PASSWORD      = 'StrongPass!2024',
  SOURCE_AUTO_POSITION = 1;
START REPLICA;

binlogファイル名やオフセットを把握する必要はありません。MySQLがGTIDセットに基づいて不足しているトランザクションを自動的に特定します。

GTIDを使用する際によくあるエラー

  • Error 1236 — Got fatal error from master:SlaveがMasterで既にpurgeされたbinlogを要求しています。対処法:Masterから再ダンプし、インポート前にSlave上でRESET MASTERを実行してから再同期します。
  • Error 1418 — This function has none of DETERMINISTICストアド関数作成時にDETERMINISTIC宣言が不足しています。関数にキーワードを追加するか、急ぎの場合は一時的にlog_bin_trust_function_creators = ONを設定します。
  • GTID重複によるSlaveの停止:ダンプを正しい順序でリストアしなかった場合によく発生します。ダンプインポートのにSlave上でRESET MASTERを実行することで回避できます。

GTIDのセットアップには従来のレプリケーションより15〜20分ほど余計にかかります。しかし、その見返りとしてフェイルオーバーが数十分から数分に短縮され、午前3時にbinlogの位置を調べなければならない状況からも解放されます。

Share: