基于docker的MySQL主从复制(replication)
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/复制到主机,然后修改配置:
master的配置在my.cnf文件中是这样的,改完后另存为/vagrant/mysql/mymaster.cnf:
slave的配置就更简单了,改完后另存为/vagrant/mysql/myslave.cnf:
slave没有必要非得用binary logging,但是如果用了,除了binary logging带来的好处以外,还能使这个slave成为其他slave的master。现在我们重新启动mysql master和slave:
在master创建一个复制用的用户:
| 
 | 
 | 
在slave用新创建的用户连接master(记得把MASTER_HOST改为自己的主机IP):
| 
 | 
 | 
如果一切正常,应该在Last_Error中能看到Can't create database 'mysql'的错误。这是因为slave也是像master一样正常地启动,mysql数据库已经被创建了,所以不能再将master的mysql数据库同步过来。有4种解决办法:
- 通过在slave上运行SQL来跳过这个复制操作的方式来实现。在slave上运行: 1234STOP SLAVE;SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;START SLAVE;SHOW SLAVE STATUS\G- 不出意外的话,上面的错误应该已经换成了其他错误(例如: - Duplicate entry 'row_evaluate_cost' for key 'PRIMARY'),都是跟mysql这个数据库有关。反复运行上面的SQL直至错误消失。
- 通过在slave上面配置log文件名及位置的方式来实现。在master上运行: 1docker exec -it master mysql -uroot -p12345612FLUSH TABLES WITH READ LOCK; --防止有人对master做更新操作使Position持续变化,先锁表SHOW MASTER STATUS\G- 可以看到 - File: mysql-bin.000003和- Position: 154这样的行。删掉这个旧的slave并重新启动一个新的容器,然后运行:1234567docker rm -f slavedocker run -d \--name=slave \-e MYSQL_ROOT_PASSWORD=123456 \-v /vagrant/mysql/myslave.cnf:/etc/mysql/my.cnf \mysql:5.7docker exec -it slave mysql -uroot -p1234561234STOP 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: Yes和- Slave_SQL_Running: Yes。这两项说明我们的slave已经成功启动了。如果先前锁了master的表,记得在master上运行- UNLOCK TABLES;来恢复。
- 通过不记录 - mysql数据库binary logging的方式来实现。既然- mysql不在binary logging里,那它也无法被同步到slave上。在- /vagrant/mysql/mymaster.cnf里增加一个参数,如果有多个数据库,可以复制多行:1binlog-ignore-db=mysql- 删除master和slave容器然后再重新创建之: 12345678910111213docker rm -f masterdocker rm -f slavedocker run -d \--net=host \--name=master \-e MYSQL_ROOT_PASSWORD=123456 \-v /vagrant/mysql/mymaster.cnf:/etc/mysql/my.cnf \mysql:5.7docker 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_Running和- Slave_SQL_Running。
- 通过不复制 - mysql数据库binary logging的方式来实现。这种方式很类似上面一种方法,只不过配置在slave端而非master端而已。在- /vagrant/mysql/myslave.cnf里增加一个参数,删除master和slave容器然后再重新创建之:1replicate-ignore-db=mysql- 其余操作同方法3。 
既然slave已经成功启动了,我们便可以测试一下。看看在master上创建一个新数据库是否能同步到slave上:
GTIDs方式
下面介绍一下GTIDs方式的主从复制方法。需要修改/vagrant/mysql/mymaster.cnf:
还需要修改/vagrant/mysql/myslave.cnf(MySQL 5.7.4及之前的版本还需要开启log-bin):
启动容器,创建复制的用户都和上面一样,在slave增加MASTER_AUTO_POSITION参数来连接master(记得把MASTER_HOST改为自己的主机IP):
| 
 | 
 | 
搞定!这样就不需要用到MASTER_LOG_FILE和MASTER_LOG_POS了,省事儿啊。在START SLAVE之前master的其它更新也都会被同步到slave。
其他技巧
最后再介绍一些实用技巧:
- 如果master已经有数据了,怎么新增slave:可以先把master的数据导入到slave,再启动slave。具体可以参考这里。
- 如果已经有主从复制了,怎么增加slave:思路同上,不过不需要使用master的数据,直接用已有的slave数据就可以了。不需要停止master,新slave使用新的server-id。具体可以参考这里。
- slave设置只读操作:在 - /vagrant/mysql/myslave.cnf里增加参数即可。12read-only=1 # 除非有SUPER权限,否则只读super-read-only=1 # SUPER权限也是只读
- 前面介绍的都是主从,如果需要slave也能同步到master就要设置主主复制:也就是说反过来再做一遍。 
- 当slave比较多得时候,master的负载可能会成为问题。可以用主从多级复制:以slave为master来再引入新的slave。

