基于Docker搭建MySQL主从复制架构实践
下面,直接进入主题,如果还没接触过Docker的朋友,建议先去学习一下Docker。Docker对于个人开发者来说也是一大神器,会大大降低我们很多学习成本,比如搭建各种组件环境等。
创建网段
因为是搭建一个主从复制架构,master和slave数据库都需要进行网络通信,所以需要保证master和slave的网络是通的。
我们可以基于Docker模拟一个内网网段。如下,创建mysql-replication网络:
docker network create mysql-replication
查看刚才创建的网络:
docker network inspect mysql-replication
输出内容如下:
[
{
"Name":"mysql-replication",
"Id":"8e0e22b80aff26987986959c2814fa90a2b390f2de36020f7c245d79a308bc91",
"Created":"2021-02-20T10:08:28.364053029+08:00",
"Scope":"local",
"Driver":"bridge",
"EnableIPv6":false,
"IPAM":{
"Driver":"default",
"Options":{},
"Config":[
{
"Subnet":"172.29.0.0/16",
"Gateway":"172.29.0.1"
}
]
},
"Internal":false,
"Attachable":false,
"Ingress":false,
"ConfigFrom":{
"Network":""
},
"ConfigOnly":false,
"Containers":{},
"Options":{},
"Labels":{}
}
]
这样我们就创建好了网络,模拟了一个内网网段。
创建mysql容器
1.拉取mysql镜像
这里我们以拉去5.7版本为例
docker pull mysql:5.7
2.启动两个mysql容器
启动mysql容器,命名为mysql-a
docker run -d \
--network=mysql-replication \
--name=mysql-a \
-p 3308:3306 \
-v /root/mysql-replication-test/mysql-a/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root mysql:5.7
启动mysql容器,命名为mysql-b
docker run -d \
--network=mysql-replication \
--name=mysql-b \
-p 3309:3306 \
-v /root/mysql-replication-test/mysql-b/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root mysql:5.7
这样,我们就启动了两个mysql容器了,并且都处于mysql-replication这个网络下。
搭建主从
我们让mysql-a担任Master角色
数据准备
查看mysql-a、mysql-b的ip
docker inspect mysql-a | grep "IPAddress"
docker inspect mysql-b | grep "IPAddress"
mysql-a的ip=172.29.0.2,mysql-b的ip=172.29.0.3
修改master数据库配置
进入mysql容器修改配置
docker exec-ti mysql-a bash
然后修改配置:
设置mysql id
开启binlog日志
vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file =/var/run/mysqld/mysqld.pid
socket =/var/run/mysqld/mysqld.sock
datadir =/var/lib/mysql
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
### 配置mysql id,要确保主从的id不同
server-id=100
### 开启binlog日志
log-bin=mysql-bin
重启mysql-a容器,让配置生效
docker restart mysql-a
重新登录mysql-a的mysql,执行show master status查看当前状态,后面配置slave mysql需要用到,这里我们查到:
我们后面会用到File和Position字段的值
master数据库创建同步用户
在master mysql上面,创建一个用户,用于同步数据。
# 创建用户slave_01,密码是salve_01,允许ip为172.29.0.*的机器同步数据
create user 'slave_01'@'172.29.0.%' identified by'slave_01';
# 授权
grant replication slave on *.* to 'slave_01'@'172.29.0.%';
# 生效
flush privileges;
备份master数据库到slave数据库
如果master数据库没有需要同步备份到从数据库的数据,这一步可以不处理。
在mysql-a容器里,执行命令:
mysqldump --single-transaction -uroot -p --master-data=2-A>dump.sql
这样在当前目录就可以看到了dump.sql,把dump.sql文件移动到和宿主机共享的目录:
mv dump.sql /var/lib/mysql
退出mysql-a容器,到dump.sql目录拷贝dump.sql到容器mysql-b:
docker cp dump.sql b2a06688fe70:/
其中,b2a06688fe70是mysql-b容器的id。
登录mysql-b,导入备份数据:
docker exec-ti mysql-b bash
# 登录mysql
mysql -u root -p
# 登录上mysql后,执行备份sql
source /dump.sql
这样,我们就把mysql-a的数据备份到mysql-b。
修改slave数据库配置
修改mysql配置文件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file =/var/run/mysqld/mysqld.pid
socket =/var/run/mysqld/mysqld.sock
datadir =/var/lib/mysql
#log-error = /var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address = 127.0.0.1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# 设置salve mysql id,不和master重复
server-id=101
# 开启并配置slave的binlog文件名称
log-bin=mysql-slave-bin
# 配置binlog中继文件
log-bin=mysql-slave-bin
重启mysql-b,让配置生效:
docker restart mysql-b
配置从库从主库复制数据:
change master to master_host='172.29.0.2',
master_user='slave_01',
master_password='slave_01',
master_port=3306,
master_log_file='mysql-bin.000001',
master_log_pos=154,
master_connect_retry=30
master_host:master的host
master_user:用于复制数据用的账号
master_password:用户复制数据用的账号密码
master_port:master的端口
masterlogfile:master的binlog的File名称,在show master status命令可以查出来
masterlogpost:master的binlog的Position,在show master status命令可以查出来
masterconnectretry:连接重试时间,单位:秒
开启主从复制:
start slave;
查看开启状态:
show slave status \G;
如果看到:
SlaveSQLRunning: YES
SlaveIORunning: YES
SlaveSQLRunning_State: Slave has read all relay log; waiting for more updates
那么主从复制就已经配置完成了。
验证是否完成主从复制
我们可以在mysql-a添加一条数据,然后到mysql-b上面看,是不是有新添加的数据。
有就表示主从复制已经完成,没有就表示主从复制没有完成。
半同步复制优化
我们到这里是搭建好了MySQL的主从复制,但是有个问题,它是异步复制的。在一些极端条件下会导致数据问题。比如master插入一条数据并成功提交事务了,这个时候binlog还没有同步到slave,master就宕机了。当主从发生切换后,新的master是没有旧的master新插入的那条数据的。
解决这个问题,业界有个方案:半同步。这里不展开说半同步的原理,只说怎么实现半同步主从复制。
安装半同步插件
在master上安装半同步插件:
# 安装插件
install plugin rpl_semi_sync_master soname 'semisync_master.so';
# 开启插件
setglobal rpl_semi_sync_master_enabled=1;
# 查询是否开启了半同步,Rpl_semi_sync_master_status=ON表示半同步开启
show status like 'Rpl%';
配置mysql重启后加载半同步插件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 半同步插件加载
plugin-load=rpl_semi_sync_master=semisync_master.so
# 打开主的半同步
rpl_semi_sync_master_enabled=1
在slave上安装半同步插件:
# 安装插件
install plugin rpl_semi_sync_slave soname 'semisync_slave.so'
# 开启插件
setglobal rpl_semi_sync_slave_enabled=1;
# 重启同步线程
stop slave io_thread;
start slave_io_thread;
# 查询是否开启了半同步,Rpl_semi_sync_slave_status=ON表示半同步开启
show status like 'Rpl%';
配置mysql重启加载半同步插件:
vim /etc/mysql/mysql.conf.d/mysqld.cnf
# 从库半同步插件加载
plugin-load=rpl_semi_sync_slave=semisync_slave.so
# 打开从的半同步
rpl_semi_sync_slave_enabled=1
到这里就开启了半同步。
从库设置为只读
我们配置主从复制的时候,一般是不允许从库写入的,这样会导致数据不一致,因为从库数据无法同步给到主库。所以我们一般会把从库设置为只读。
# 查看只读状态,read_only=ON是开启,OFF是关闭
show global variables like "%read_only%";
# 给所有表加上读锁
flush tables with read lock;
# 设置只读
setglobal read_only=1;
# 如果想解除只读,执行下面两行命令即可
unlock tables;
setglobal read_only=0;
注意点
我们安装的mysql容器,是没有vim工具的,需要我们自行安装,安装步骤:
apt-get update
apt-get install vim