MySQL の GTID レプリを replicate-do-db でフィルタすると欠番になる?

スレーブ側で--replicate-do-や--replicate-ignore-などのルールを使ってフィルタリングをすると、GTIDに欠番ができて、連番が連続しなくなるため、SHOW SLAVE STATUSの出力が大変なことになってしまう。GTIDを用いるときは、フィルタリングしないのほうが無難である。

漢(オトコ)のコンピュータ道: MySQLレプリケーションの運用が劇的変化!!GTIDについて仕組みから理解する

replicate-do-db などでスレーブでフィルタしていると GTID に欠番が生じてスレーブの Executed_Gtid_Set がすごいことになる。ということだと思うのですが、GTID はトランザクション単位で採番されるものの replicate-do-db とかはステートメント単位とかだと思うので(1つのトランザクションの中にフィルタされる更新とされない更新が混ざることがある)、直感的にはトランザクションの一部がフィルタされたときはマスターとスレーブでトランザクションの内容に差異が生じて、全部フィルタされたときも GTID が欠番にはならずに空のトランザクションになりそうな気がしたので、試してみました。

試したバージョンは次の通り。

mysqld --version
/usr/sbin/mysqld  Ver 8.0.11 for Linux on x86_64 (MySQL Community Server - GPL)

docker-compose.yml

version: '3.4'

services:
  sv01:
    image: mysql:8
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
      MYSQL_DATABASE: test
    networks:
      mysql:
        ipv4_address: 192.168.88.11
    command:
      - --default_authentication_plugin=mysql_native_password
      - --skip-name-resolve
      - --character-set-server=utf8
      - --innodb-file-per-table
      - --log-bin=mysql-bin
      - --sync-binlog=1
      - --relay-log=relay-bin
      - --log-slave-updates
      - --skip-slave-start
      - --binlog-format=row
      - --replicate-do-db=test
      - --slave-exec-mode=IDEMPOTENT
      - --master-info-repository=TABLE
      - --relay-log-info-repository=TABLE
      - --relay-log-recovery=ON
      - --gtid-mode=ON
      - --enforce-gtid-consistency
      - --server-id=1

  sv02:
    image: mysql:8
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
      MYSQL_DATABASE: test
    networks:
      mysql:
        ipv4_address: 192.168.88.12
    command:
      - --default_authentication_plugin=mysql_native_password
      - --skip-name-resolve
      - --character-set-server=utf8
      - --innodb-file-per-table
      - --log-bin=mysql-bin
      - --sync-binlog=1
      - --relay-log=relay-bin
      - --log-slave-updates
      - --skip-slave-start
      - --binlog-format=row
      - --replicate-do-db=test
      - --slave-exec-mode=IDEMPOTENT
      - --master-info-repository=TABLE
      - --relay-log-info-repository=TABLE
      - --relay-log-recovery=ON
      - --gtid-mode=ON
      - --enforce-gtid-consistency
      - --server-id=2

networks:
  mysql:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.88.0/24

docker-compose で環境を立ち上げる。

docker-compose up
docker-compose exec sv01 bash
docker-compose exec sv02 bash

レプリケーション用のユーザーを作成。

# [sv01/sv02]
mysql mysql -v <<'SQL'
CREATE USER 'repl'@'%' IDENTIFIED BY 'pass';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
SQL

バイナリログがわかりやすくなるように reset master しとく。

# [sv01/sv02]
mysql mysql -v <<'SQL'
reset master;
SQL

レプリケーションを開始。

# [sv02]
mysql mysql -v <<'SQL'
change master to
  MASTER_HOST = '192.168.88.11',
  MASTER_USER = 'repl',
  MASTER_PASSWORD = 'pass',
  MASTER_AUTO_POSITION = 1;
start slave;
SQL

--replicate-do-db=test により test データベース以外はスレーブでフィルタされるようになっているので、適当に別のデータベースを作るなどしてマスターを更新します。

/* [sv01] */
use test
create table t (id int not null primary key, no int not null);
insert into t values (1, 111);

/* [sv01/sv02] */
select * from t;

/* [sv01] */
create database hoge;
use hoge
create table h (id int not null primary key, no int not null);
insert into h values (1, 111);

/* [sv01] */
use test
insert into t values (2, 222);

GTID を見てみます。

/* [sv01/sv02] */
show master status \G
show slave status \G

見た感じ欠番が生じてる雰囲気はありません。次のように mysqlbinlog を見比べてみても、

mysqlbinlog mysql-bin.000001 --include-gtids=6930f785-7376-11e8-9b24-0242c0a8580b:5 --base64-output=DECODE-ROWS -v

sv01

BEGIN
/*!*/;
# at 1197
#180619  5:22:14 server id 1  end_log_pos 1245 CRC32 0xb5b056b1         Table_map: `hoge`.`h` mapped to number 107
# at 1245
#180619  5:22:14 server id 1  end_log_pos 1289 CRC32 0xe5c002ce         Write_rows: table id 107 flags: STMT_END_F
### INSERT INTO `hoge`.`h`
### SET
###   @1=1
###   @2=111
# at 1289
#180619  5:22:14 server id 1  end_log_pos 1320 CRC32 0xf366fedf         Xid = 122
COMMIT/*!*/;

sv02

BEGIN
/*!*/;
# at 1265
#180619  5:22:14 server id 1  end_log_pos 1332 CRC32 0x7b096e01         Query   thread_id=17    exec_time=0     error_code=0
SET TIMESTAMP=1529385734/*!*/;
COMMIT

スレーブでフィルタされた処理は空のトランザクションとなって記録されているようです。

ので replicate-do-db などを用いてフィルタしてもスレーブで GTID に欠番がでることはなさそうです。

さいごに

参考にした記事はだいぶ古く(3年半ぐらい前)、バージョンも 5.6 とかなので、今回試した 8.0.11 だとその辺の事情も変わってるのかもしれません。

現代は GTID レプリケーションと replicate-do-db などを併用しても問題ない、と思います。