文章目录
  1. 1. binary logging方式
  2. 2. GTIDs方式
  3. 3. 其他技巧

MySQL复制技术可以异步地将主数据库的数据同步到从数据库。根据设置可以复制所有数据库、指定数据库甚至可以指定表。本文的主要内容是怎样用docker从零开始搭建mysql主从复制环境,支持binary logging方式和GTIDs方式。

MySQL 5.7支持多种复制方法。传统的方法是master使用binary logging,slave复制并重放日志中的事件。另一种方法是利用GTIDs(global transaction identifiers)将所有未执行的事务在slave重放。

binary logging方式

接下来先用传统的方法试一下。使用MySQL 5.7镜像,将/etc/mysql/conf.d/复制到主机,然后修改配置:

1
2
docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name=mysql mysql:5.7
docker cp mysql:/etc/mysql/my.cnf my.cnf

master的配置在my.cnf文件中是这样的,改完后另存为/vagrant/mysql/mymaster.cnf

1
2
3
[mysqld]
log-bin=mysql-bin # 使用binary logging,mysql-bin是log文件名的前缀
server-id=1 # 唯一服务器ID,非0整数,不能和其他服务器的server-id重复

slave的配置就更简单了,改完后另存为/vagrant/mysql/myslave.cnf

1
2
[mysqld]
server-id=2 # 唯一服务器ID,非0整数,不能和其他服务器的server-id重复

slave没有必要非得用binary logging,但是如果用了,除了binary logging带来的好处以外,还能使这个slave成为其他slave的master。现在我们重新启动mysql master和slave:

1
2
3
4
5
6
7
8
9
10
11
12
docker rm -f mysql
docker run -d \
--net=host \
--name=master \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /vagrant/mysql/mymaster.cnf:/etc/mysql/my.cnf \
mysql:5.7
docker run -d \
--name=slave \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /vagrant/mysql/myslave.cnf:/etc/mysql/my.cnf \
mysql:5.7

在master创建一个复制用的用户:

1
docker exec -it master mysql -uroot -p123456

1
2
CREATE USER 'repl'@'%' IDENTIFIED BY '123456'; -- '%'意味着所有的终端都可以用这个用户登录
GRANT SELECT,REPLICATION SLAVE ON *.* TO 'repl'@'%'; -- SELECT权限是为了让repl可以读取到数据,生产环境建议创建另一个用户

在slave用新创建的用户连接master(记得把MASTER_HOST改为自己的主机IP):

1
docker exec -it slave mysql -uroot -p123456

1
2
3
CHANGE MASTER TO MASTER_HOST='192.168.33.32', MASTER_USER='repl', MASTER_PASSWORD='123456';
START SLAVE;
SHOW SLAVE STATUS\G -- \G用来代替";",能把查询结果按键值的方式显示

如果一切正常,应该在Last_Error中能看到Can't create database 'mysql'的错误。这是因为slave也是像master一样正常地启动,mysql数据库已经被创建了,所以不能再将master的mysql数据库同步过来。有4种解决办法:

  1. 通过在slave上运行SQL来跳过这个复制操作的方式来实现。在slave上运行:

    1
    2
    3
    4
    STOP SLAVE;
    SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
    START SLAVE;
    SHOW SLAVE STATUS\G

    不出意外的话,上面的错误应该已经换成了其他错误(例如:Duplicate entry 'row_evaluate_cost' for key 'PRIMARY'),都是跟mysql这个数据库有关。反复运行上面的SQL直至错误消失。

  2. 通过在slave上面配置log文件名及位置的方式来实现。在master上运行:

    1
    docker exec -it master mysql -uroot -p123456
    1
    2
    FLUSH TABLES WITH READ LOCK; --防止有人对master做更新操作使Position持续变化,先锁表
    SHOW MASTER STATUS\G

    可以看到File: mysql-bin.000003Position: 154这样的行。删掉这个旧的slave并重新启动一个新的容器,然后运行:

    1
    2
    3
    4
    5
    6
    7
    docker rm -f slave
    docker run -d \
    --name=slave \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /vagrant/mysql/myslave.cnf:/etc/mysql/my.cnf \
    mysql:5.7
    docker exec -it slave mysql -uroot -p123456
    1
    2
    3
    4
    STOP SLAVE;
    CHANGE MASTER TO MASTER_HOST='192.168.33.32', MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=154;
    START SLAVE;
    SHOW SLAVE STATUS\G

    我们将会看到Slave_IO_Running: YesSlave_SQL_Running: Yes。这两项说明我们的slave已经成功启动了。如果先前锁了master的表,记得在master上运行UNLOCK TABLES;来恢复。

  3. 通过不记录mysql数据库binary logging的方式来实现。既然mysql不在binary logging里,那它也无法被同步到slave上。在/vagrant/mysql/mymaster.cnf里增加一个参数,如果有多个数据库,可以复制多行:

    1
    binlog-ignore-db=mysql

    删除master和slave容器然后再重新创建之:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    docker rm -f master
    docker rm -f slave
    docker run -d \
    --net=host \
    --name=master \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /vagrant/mysql/mymaster.cnf:/etc/mysql/my.cnf \
    mysql:5.7
    docker run -d \
    --name=slave \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -v /vagrant/mysql/myslave.cnf:/etc/mysql/my.cnf \
    mysql:5.7

    然后根据上文所述在master创建一个复制用的用户并在slave用新创建的用户连接master,最后观察Slave_IO_RunningSlave_SQL_Running

  4. 通过不复制mysql数据库binary logging的方式来实现。这种方式很类似上面一种方法,只不过配置在slave端而非master端而已。在/vagrant/mysql/myslave.cnf里增加一个参数,删除master和slave容器然后再重新创建之:

    1
    replicate-ignore-db=mysql

    其余操作同方法3。

既然slave已经成功启动了,我们便可以测试一下。看看在master上创建一个新数据库是否能同步到slave上:

1
2
docker exec master mysql -uroot -p123456 -e "CREATE DATABASE ggg"
docker exec slave mysql -uroot -p123456 -e "SHOW DATABASES"

GTIDs方式

下面介绍一下GTIDs方式的主从复制方法。需要修改/vagrant/mysql/mymaster.cnf

1
2
3
4
5
[mysqld]
log-bin=mysql-bin
server-id=1
gtid-mode=on
enforce-gtid-consistency=true

还需要修改/vagrant/mysql/myslave.cnf(MySQL 5.7.4及之前的版本还需要开启log-bin):

1
2
3
4
5
[mysqld]
server-id=2
replicate-ignore-db=mysql
gtid-mode=on
enforce-gtid-consistency=true

启动容器,创建复制的用户都和上面一样,在slave增加MASTER_AUTO_POSITION参数来连接master(记得把MASTER_HOST改为自己的主机IP):

1
docker exec -it slave mysql -uroot -p123456

1
2
3
CHANGE MASTER TO MASTER_HOST='192.168.33.32', MASTER_USER='repl', MASTER_PASSWORD='123456', MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G

搞定!这样就不需要用到MASTER_LOG_FILEMASTER_LOG_POS了,省事儿啊。在START SLAVE之前master的其它更新也都会被同步到slave。

其他技巧

最后再介绍一些实用技巧:

  1. 如果master已经有数据了,怎么新增slave:可以先把master的数据导入到slave,再启动slave。具体可以参考这里
  2. 如果已经有主从复制了,怎么增加slave:思路同上,不过不需要使用master的数据,直接用已有的slave数据就可以了。不需要停止master,新slave使用新的server-id。具体可以参考这里
  3. slave设置只读操作:在/vagrant/mysql/myslave.cnf里增加参数即可。

    1
    2
    read-only=1 # 除非有SUPER权限,否则只读
    super-read-only=1 # SUPER权限也是只读

  4. 前面介绍的都是主从,如果需要slave也能同步到master就要设置主主复制:也就是说反过来再做一遍。

  5. 当slave比较多得时候,master的负载可能会成为问题。可以用主从多级复制:以slave为master来再引入新的slave。
文章目录
  1. 1. binary logging方式
  2. 2. GTIDs方式
  3. 3. 其他技巧