Amazon RDS for MySQL でマルチソースレプリケーションを試してみた

2024.02.29

こんにちは、森田です。

以下のアップデートでAmazon RDS for MySQL でマルチソースレプリケーションが可能となりました。

本記事では、実際に Amazon RDS for MySQL でのマルチソースレプリケーションの手順をご紹介します。

やってみた

今回は2つのRDSをソースとして、1つのRDSへレプリケーションを行います。

前提条件

本記事では、RDSインスタンス3つ(database-1, database-2, target)は作成済みとします。

作成したインスタンスは、マルチソースレプリケーション可能な 8.0.35 で作成しています。

パラメータグループは以下スクリプトで作成済みとします。

pg_create.sh

pg_name="rds-multi-source-replication"

aws rds create-db-parameter-group \
    --db-parameter-group-name $pg_name \
    --db-parameter-group-family mysql8.0 \
    --description $pg_name

バイナリログの有効化

バイナリログを用いてのレプリケーションを行っているため、バイナリログの有効化が必要となります。

以下をスクリプトを実行し、バイナリログが有効となるようパラメータグループを変更します。

binlog_enable.sh

pg_name="rds-multi-source-replication"

aws rds modify-db-parameter-group \
    --db-parameter-group-name $pg_name \
    --parameters "ParameterName=log_bin,ParameterValue=ON,ApplyMethod=immediate" \
    --parameters "ParameterName=binlog_format,ParameterValue=ROW,ApplyMethod=immediate"

ソースインスタンス側の設定

レプリケーションを行うため、ソースインスタンス側で以下の設定が必要です。

  • ターゲットインスタンスがレプリケーションを行う際に利用するユーザ作成
  • バイナリログ保持期間がnullとなっているため、任意の値の設定
  • ソース DB インスタンスへの書き込み操作のブロック(read-only パラメータを ON)

これらを各ソースインスタンス側で設定を行うのは数が増えると面倒なため、スクリプトで行います。

setting.sh

endpoints=(
  "database-1.xxxx.ap-northeast-1.rds.amazonaws.com"
  "database-2.xxxx.ap-northeast-1.rds.amazonaws.com"
)
passwords=(
  "xxx"
  "xxx"
)
pg_names=(
  "rds-multi-source-replication"
  "rds-multi-source-replication"
)

user_password="hogefuga1234"

length=${#endpoints[@]}

for (( i=0; i<$length; i++ ))
do
  endpoint=${endpoints[$i]}
  password=${passwords[$i]}
  pg_name=${pg_names[$i]}

mysql -h $endpoint -P 3306 -u admin -p$password -e \
"CREATE USER 'repl_user'@'%' IDENTIFIED BY '$user_password'; \
GRANT REPLICATION SLAVE ON <em>.</em> TO 'repl_user'@'%'; \
CALL mysql.rds_set_configuration('binlog retention hours', 24);
"

aws rds modify-db-parameter-group --db-parameter-group-name $pg_name \
--parameters "ParameterName=read_only,ParameterValue=1,ApplyMethod=immediate"

done

上記を利用する際には、endpointspasswordspg_namesなどを環境にあった値に変更する必要があります。

ソースインスタンスからターゲットインスタンスへのダンプ

mysqldump を使用して、ソース DB インスタンスからターゲットインスタンスにデータベースをコピーします。

また、レプリケーションを行う際にbinlogFileとPostionが必要となるため、出力を行います。

以下スクリプトで上記をまとめて実行していきます。

dump.sh

endpoints=(
  "database-1.xxxx.ap-northeast-1.rds.amazonaws.com"
  "database-2.xxxx.ap-northeast-1.rds.amazonaws.com"
)
passwords=(
  "xxx"
  "xxx"
)
databases=(
  "userdatabase"
  "productdatabase"
)

target_endpoint="target.xxxx.ap-northeast-1.rds.amazonaws.com"
target_password="hogefuga1234"

length=${#endpoints[@]}

for (( i=0; i<$length; i++ ))
do
  endpoint=${endpoints[$i]}
  password=${passwords[$i]}
  database=${databases[$i]}

mysqldump --databases $database \
 --compress \
 --set-gtid-purged=OFF \
 --order-by-primary \
 -u admin \
 -p$password \
 --host=$endpoint  | mysql \
 --host=$target_endpoint \
 --port=3306 \
 -u admin \
 -p$target_password

mysql -h $endpoint -P 3306 -u admin -p$password -e "SHOW MASTER STATUS;"

done

実行結果

+----------------------------+----------+--------------+------------------+-------------------+
| File                       | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------------------+----------+--------------+------------------+-------------------+
| mysql-bin-changelog.000066 |      534 |              |                  |                   |
+----------------------------+----------+--------------+------------------+-------------------+

+----------------------------+----------+--------------+------------------+-------------------+
| File                       | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------------------+----------+--------------+------------------+-------------------+
| mysql-bin-changelog.000067 |      534 |              |                  |                   |
+----------------------------+----------+--------------+------------------+-------------------+

ターゲットインスタンス側の設定

rds_set_external_source_for_channelrds_start_replication_for_channelのストアドプロシージャを使って、レプリケーションを有効化します。

先ほど取得したbinlogの情報を以下スクリプトの上部に記載して実行します。

endpoints=(
  "database-1.xxxx.ap-northeast-1.rds.amazonaws.com"
  "database-2.xxxx.ap-northeast-1.rds.amazonaws.com"
)
bin_files=(
  "mysql-bin-changelog.000066"
  "mysql-bin-changelog.000067"
)
positions=(
  "534"
  "534"
)

target_endpoint="target.xxx.ap-northeast-1.rds.amazonaws.com"
target_password="hogefuga1234"

user_password="hogefuga1234"

length=${#endpoints[@]}

for (( i=0; i<$length; i++ ))
do
  source_endpoint=${endpoints[$i]}
  bin_file=${bin_files[$i]}
  bin_position=${positions[$i]}

mysql -h $target_endpoint -P 3306 -u admin -p$target_password -e \
"\
CALL mysql.rds_set_external_source_for_channel( \
    '$source_endpoint', \
    3306, \
    'repl_user', \
    '$user_password', \
    '$bin_file', \
    $bin_position, \
    0, \
    'source$i'\
);\
CALL mysql.rds_start_replication_for_channel('source$i');\
"
done

動作確認

ターゲットインスタンスでSHOW REPLICA STATUS\Gを実行すると、現在のレプリケーション状況が確認できます。

mysql> SHOW REPLICA STATUS\G
*************************** 1. row ***************************
             Replica_IO_State: Waiting for master to send event
                  Source_Host: database-1.czdia394lloi.ap-northeast-1.rds.amazonaws.com
                  Source_User: repl_user
                  Source_Port: 3306
                Connect_Retry: 60
              Source_Log_File: mysql-bin-changelog.000117
          Read_Source_Log_Pos: 157
               Relay_Log_File: relaylog-source0.000105
                Relay_Log_Pos: 279
        Relay_Source_Log_File: mysql-bin-changelog.000117
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
*************************** 2. row ***************************
             Replica_IO_State: Waiting for master to send event
                  Source_Host: database-2.xxxx.ap-northeast-1.rds.amazonaws.com
                  Source_User: repl_user
                  Source_Port: 3306
                Connect_Retry: 60
              Source_Log_File: mysql-bin-changelog.000118
          Read_Source_Log_Pos: 534
               Relay_Log_File: relaylog-source1.000105
                Relay_Log_Pos: 656
        Relay_Source_Log_File: mysql-bin-changelog.000118
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes

ソースインスタンスへのinsert処理

ソースインスタンスは、read_onlyとなっているため、read_onlyをオフにします。

pg_name="rds-multi-source-replication"

aws rds modify-db-parameter-group --db-parameter-group-name $pg_name \
--parameters "ParameterName=read_only,ParameterValue=0,ApplyMethod=immediate"

そのあとで、insertの実行を行います。

mysql> INSERT INTO users (name, email, password) VALUES \
    -> ('Morita', 'morita@example.com', 'morita123'); 
Query OK, 1 row affected (0.15 sec)

ターゲットインスタンスのテーブル確認

ターゲットインスタンスに接続し、先ほどinsertを行ったusersテーブルを取得してみると、以下のようにレプリケーションが行えていることが確認できます。

mysql> select * from users;
+----+----------------+--------------------+-----------+---------------------+
| id | name           | email              | password  | created_at          |
+----+----------------+--------------------+-----------+---------------------+
|  1 | Alice Smith    | alice@example.com  | alice123  | 2024-02-29 09:42:53 |
|  2 | Bob Johnson    | bob@example.com    | bob123    | 2024-02-29 09:42:53 |
|  3 | Carol Williams | carol@example.com  | carol123  | 2024-02-29 09:42:53 |
|  4 | Morita         | morita@example.com | morita123 | 2024-02-29 11:28:41 |
+----+----------------+--------------------+-----------+---------------------+

最後に

レプリケーションを行うために少し前準備が必要ですが、データ分析やバックアップとしての用途としては活用できそうな機能となっています。

複数のインスタンスに対して処理を行う必要があるため、ぜひ今回作成したスクリプトを活用してみてください。

おまけ

何度か動作確認のため、レプリケーションの停止や削除を行なったため、その際に利用したストアドプロシージャも載せておきます。

レプリケーションの停止

mysql> CALL mysql.rds_stop_replication_for_channel('チャンネル名');
+--------------------------------------------------------+
| Message                                                |
+--------------------------------------------------------+
| Replication for channel 'チャンネル名' is down or disabled. |
+--------------------------------------------------------+

レプリケーションの削除(リセット)

mysql> CALL mysql.rds_reset_external_source_for_channel  ("チャンネル名");
+---------------------------------------------------------+
| message                                                 |
+---------------------------------------------------------+
| Replication has been reset for channel 'チャンネル名' |
+---------------------------------------------------------+