0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【備忘】MySQLのリードレプリカ機能を試す

0
Posted at

はじめに

MySQLをはじめとしたRDBにはリードレプリカという機能があり、内容を複製した読み込み専用のデータベースを作成することができます。
今回はMySQLでリードレプリカを作ってみようと思います。

リードレプリカを作成する

リードレプリカは以下の手順で作成します。

  • レプリカ用のデータベースを作成する
  • ライターのデータベースをレプリカのデータベースにダンプ&リストア
  • レプリケーション開始のコマンド

今回はdocker-composeでレプリケーションを作成してみます。

docker-compose.yaml

こちらがdocker-compose.yamlです。

Writer/Readerの両DBのmysqld起動時のコマンド引数で--server-idを付与しています。これは各DBサーバに識別子をつけることでどのDBで何が起きているのかを記録するために利用します。ReaderとWriterで被っていなければ必ずしも1と2でなくても問題ありません。

また、GTIDモードをONにするオプションも付与しています。GTIDとは各トランザクションに付与される識別子です。Readerは適用済みのGTIDを記録しておき、まだ持っていないトランザクションだけをWriterから受け取り、反映します。
--enforce-gtid-consistency=ONは、1つのGTIDで安全に記録できない更新を禁止するオプションで、GTIDモードを使う際には必須のようです。

services:
  db:
    container_name: mysql
    image: mysql:8.0.36
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: hogehoge
      MYSQL_DATABASE: mydatabase
    command:
      - --server-id=1
      - --log-bin=binlog
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 1s
      timeout: 5s
      retries: 5

  db-replica:
    container_name: mysql-replica
    image: mysql:8.0.36
    volumes:
      - db_replica_data:/var/lib/mysql
      - ./Docker/mysql/replica-init.sh:/docker-entrypoint-initdb.d/replica-init.sh:ro
    environment:
      MYSQL_ROOT_PASSWORD: hogehoge
      REPLICATION_SOURCE_HOST: mysql
      REPLICATION_SOURCE_USER: root
      REPLICATION_SOURCE_PASSWORD: hogehoge
      REPLICATION_DATABASES: mydatabase
    command:
      - --server-id=2
      - --log-bin=binlog
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --relay-log=relay-bin
    ports:
      - "3307:3306"
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 1s
      timeout: 5s
      retries: 30
volumes:
  db_data:
  db_replica_data:

replica-init.sh

init.shです。
大まかな流れは冒頭の通りです。

ダンプ時につけている--single-transactionはコマンド実行時点の状態をWriterをロックせずにダンプするために付与しています。--set-gtid-purged=ONを付与してその時点のGTIDの記録もしています。

レプリケーション開始後のSET PERSIST read_only = ON;とSET PERSIST super_read_only = ON;は、リーダーに対する更新を禁止するコマンドです。無印のread_onlyではrootユーザによる更新を許してしまうため、super_read_onlyで禁止をしてます。

#!/bin/bash
set -euo pipefail

SOURCE_HOST="${REPLICATION_SOURCE_HOST:-mysql}"
SOURCE_USER="${REPLICATION_SOURCE_USER:-root}"
SOURCE_PASSWORD="${REPLICATION_SOURCE_PASSWORD:-root}"
DATABASES="${REPLICATION_DATABASES:-mydatabase}"

LOCAL_MYSQL=(mysql --protocol=socket -uroot -p"${MYSQL_ROOT_PASSWORD}")

# ローカルのGTID履歴をクリア
echo "[replica-init] reset local GTID state"
"${LOCAL_MYSQL[@]}" -e "RESET MASTER;"

# レプリカにダンプ&リストア
echo "[replica-init] dump from ${SOURCE_HOST}: ${DATABASES}"
mysqldump -h"${SOURCE_HOST}" -u"${SOURCE_USER}" -p"${SOURCE_PASSWORD}" \
  --databases ${DATABASES} \
  --single-transaction --routines --triggers --events \
  --set-gtid-purged=ON \
  | "${LOCAL_MYSQL[@]}"

# レプリケーション開始
echo "[replica-init] start replication"
"${LOCAL_MYSQL[@]}" <<SQL
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='${SOURCE_HOST}',
  SOURCE_PORT=3306,
  SOURCE_USER='${SOURCE_USER}',
  SOURCE_PASSWORD='${SOURCE_PASSWORD}',
  SOURCE_AUTO_POSITION=1,
  GET_SOURCE_PUBLIC_KEY=1;
START REPLICA;
SET PERSIST read_only = ON;
SET PERSIST super_read_only = ON;
SQL

動作確認

というわけで動作確認してみましょう。
test2という名前のテーブルを用意し、まずはWriterとReaderに何も入ってないことを確認。

Writer
mysql> select * from test2;
Empty set (0.00 sec)
Reader
mysql> select * from test2;
Empty set (0.00 sec)

続いてWriterにデータをINSERTします。

Writer
mysql> INSERT INTO test2 (persistence_object_identifier, name) VALUES ('testUUID', 'testName');
Query OK, 1 row affected (0.01 sec)

mysql> select * from test2;
+-------------------------------+----------+
| persistence_object_identifier | name     |
+-------------------------------+----------+
| testUUID                      | testName |
+-------------------------------+----------+
1 row in set (0.01 sec)

mysql> 

Readerにも結果が反映されました!

Reader
mysql> select * from test2;
+-------------------------------+----------+
| persistence_object_identifier | name     |
+-------------------------------+----------+
| testUUID                      | testName |
+-------------------------------+----------+
1 row in set (0.00 sec)

おわりに

今回はMySQLのリードレプリカ機能を試してみました。
普段はAuroraなどのマネージドサービスで利用することが多くどうやって作るのかわかっていませんでしたが、実際に作ってみてGTIDなどの仕組みを知ると面白いですね。

ここまでご覧いただきありがとうございました!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?