0%

MySQL之Replication模式实践

在MySQL众多的集群方案中,Replication主从复制模式作为官方原生提供的功能。这里简单地介绍下并通过Docker进行实践

abstract.png

基本原理

在MySQL的Replication模式下,可以实现将一个数据库的数据同步到多台其他数据库。前者通常称之为主库,后者则被称从库。通过主从复制,一方面可以实现数据的容灾备份,另一方面通过读写分离(主库写、从库读)可以水平拓展数据库的负载能力。MySQL复制数据基本原理如下,默认采用异步复制的方式进行

  1. 在主库上发生的数据变化记录到二进制日志Binlog
  2. 从库的IO线程将主库的Binlog复制到自己的中继日志Relay log
  3. 从库的SQL线程通过读取、重放中继日志实现数据复制

figure 1.png

基于Binlog日志坐标

全新环境下的主从模式实践

现在我们利用Docker创建两个全新的MySQL实例,并建立主从关系。以便后续向主库添加数据时,可以复制到从库上。具体地,MySQL主库、从库分别使用3316、3326端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 拉取镜像
docker pull mysql:8.0

# 创建MySQL主库,设置root账号的密码为123456
docker run -d \
-p 3316:3306 \
--name mysql-Master \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0

# 创建MySQL从库,设置root账号的密码为123456
docker run -d \
-p 3326:3306 \
--name mysql-Slave-01 \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0

然后我们在MySQL主库上新建一个账号,并授予相关权限用于复制

1
2
3
4
5
6
7
8
9
# 创建ip为任意值、用户名为repl、密码为p4word的用户
create user repl@'%' identified by 'p4word';

# 授予replication slave, replication client权限
grant replication slave, replication client
# 权限可应用于所有数据库、表
on *.*
# 授权作用于ip为任意值,用户名为repl的用户
to repl@'%';

现在我们查看下账号,确认创建成功

1
2
3
4
# 查看账号信息、权限
SELECT user, host, repl_slave_priv, repl_client_priv
FROM mysql.user
where user='repl';

结果符合预期

figure 2.png

修改MySQL主库、从库中/etc/mysql路径下的配置文件my.cnf。具体地,在主库的配置文件中添加如下配置

1
2
3
4
# 启用binlog日志,并指定文件名前缀
log_bin = mysql-bin
# 自定义一个唯一的服务器ID
server_id = 16

在从库的配置文件中添加如下配置

1
2
3
4
5
6
7
8
# 启用binlog日志,并指定文件名前缀
log_bin = mysql-bin
# 自定义一个唯一的服务器ID
server_id = 26
# 指定中继文件的位置、文件名
relay_log = /etc/mysql/relayLog/mysql-relay-bin
# 从库从主库上复制的数据,也写入从库的binlog日志
log_slave_updates = 1

然后在从库下手动创建存放中继文件所需的相关目录,并修改其所有者、所属组均为mysql

1
2
3
4
5
# 创建目录
mkdir relayLog

# 修改所有者、所属组
chown mysql.mysql relayLog/

至此,重启主库、从库。一方面可以使修改后的配置文件生效,另一方面,重启MySQL会使用新的Binlog文件。然后我们查看主库状态获取Binlog文件坐标(当前正在使用文件名及偏移量)

1
2
# 查看主库的状态
show master status;

结果如下

figure 3.png

现在就可以让从库指向主库并开始复制了

1
2
3
4
5
6
7
8
9
10
11
# 让从库使用指定账号连接指定主库,并从指定Binlog文件坐标进行复制
change master to
MASTER_HOST = '192.168.1.102',
MASTER_PORT = 3316,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'p4word',
MASTER_LOG_FILE='mysql-bin.000006',
MASTER_LOG_POS=0;

# 从库启动复制
start slave;

至此,我们的MySQL主从就已经配置完成了,可通过下面两种方式检查该模式的相关状态

1
2
3
4
5
# 在主库上,查看是否存在从库的连接
show processlist;

# 查看从库复制的状态
show slave status;

现在,当我们在MySQL主库创建DataBase、Table并插入数据后,即可发现从库的也相应进行同步的数据修改

对已有数据库进行复制

上述实践是在两个数据库开始使用存放数据前先建立主从关系。但大多数情况下,我们需要对一个已有数据的数据库配置从库。这个时候,我们需要将主库已有数据先拷贝到从库中,然后再对主、从库建立同步关系。那么就如何将主库中已有数据复制到新的从库上,大致可分为两种解决方案:冷备、热备。前者需要将主库停机,然后再把主库的数据拷贝到从库上;而后者则无需关闭主库。显然生产环境下,一般不允许停止数据库。故这里我们利用mysqldump工具来实现热备,需要注意的是mysqldump工具不适用于较大数据量的场景

这里依然使用Docker启一个MySQL容器,其使用3336端口

1
2
3
4
5
docker run -d \
-p 3336:3306 \
--name mysql-Slave-02 \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0

然后类似地,按如下方式修改mysql-Slave-02的配置文件,建立中继文件所需的目录并修改其所有者、所属组均为mysql。然后重启使配置生效

1
2
3
4
5
6
7
8
# 启用binlog日志,并指定文件名前缀
log_bin = mysql-bin
# 自定义一个唯一的服务器ID
server_id = 36
# 指定中继文件的位置、文件名
relay_log = /etc/mysql/relayLog/mysql-relay-bin
# 从库从主库上复制的数据,也写入从库的binlog日志
log_slave_updates = 1

使用mysqldump工具将主库的数据导出,相关选项参数释义如下:

  • -h :数据库所在服务器的IP
  • -P :数据库所使用的端口
  • -u :用户名
  • -p :密码
  • —single-transaction :对于事务型表(例如InnoDB),使用该选项可以保证获得一致性备份;而对于非事务型表(例如MyISAM),可以使用 —lock-all-tables 选项来获得一致性备份
  • —all-databases:导出所有数据库
  • —master-data:该选项值为1时,会将Binlog文件坐标(当前正在使用文件名及偏移量)以change master to语句的形式输出到数据文件;当该选项值为2时,会将此命令注释掉
1
2
# 在MySQL容器内执行该命令导出主库数据
mysqldump -h 192.168.1.102 -P 3316 -u root -p123456 --single-transaction --all-databases --master-data=1 > /dump.sql

从下图可以看出,dump的数据文件中的Binlog文件坐标和我们在主库中通过SQL语句查询的结果一致

figure 4.jpeg

在从库mysql-Slave-02中执行dump.sql以导入数据

1
mysql -u root -p123456 < /dump.sql

至此,从库mysql-Slave-02的数据就已经被初始化完成。现在就可以通过change master to语句来让从库指向主库准备进行复制了。注意此时不需要指定Binlog文件坐标了,因为dump的数据文件中的change master to语句含有坐标信息,现在只需补充主库的IP、端口、账号、密码等信息即可

1
2
3
4
5
6
# 让从库使用指定账号连接指定主库进行复制
change master to
MASTER_HOST = '192.168.1.102',
MASTER_PORT = 3316,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'p4word';

现在,从库就可以开始复制了

1
2
# 从库启动复制
start slave;

基于GTID全局事务标示符

在传统复制方式下,对Binlog日志坐标准确性的要求非常高。一旦从库发生意外需要重新与主库建立同步,有时候很难确定出待同步部分的Binlog日志坐标。为此MySQL 5.6版本引入基于GTID全局事务标示符的复制方式,大大简化了主从的同步配置。

主库配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#开启GTID模式
gtid_mode = on

#强制GTID一致性
enforce_gtid_consistency = on

# 基于行的复制
binlog_format=row

# 启用Binlog日志,并指定文件名前缀
log_bin = mysql-bin

# 自定义一个唯一的服务器ID
server_id = 16

从库配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#开启GTID模式
gtid_mode = on

#强制GTID一致性
enforce_gtid_consistency = on

# 自定义一个唯一的服务器ID
server_id = 26

# 启用Binlog日志,并指定文件名前缀
log_bin = mysql-bin

# 基于行的复制
binlog_format=row

# 指定中继文件的位置、文件名
relay_log = /etc/mysql/relayLog/mysql-relay-bin

# 从库从主库上复制的数据,也写入从库的binlog日志
log_slave_updates = 1

#relay log
skip_slave_start=1

修改从库中继文件所需的目录并修改其所有者、所属组均为mysql,并重启主库、从库使配置生效。然后按如前文所述的方式在主库创建账号用于复制。现在就可以通过change master to语句来让从库指向主库准备进行复制了,当然此时就不需要指定Binlog文件坐标了。与此同时,对于在执行start slave前主库中已经存在的数据,也会被复制到从库中

1
2
3
4
5
6
7
8
9
# 让从库使用指定账号连接指定主库进行复制
change master to
MASTER_HOST = '192.168.1.102',
MASTER_PORT = 3316,
MASTER_USER = 'repl',
MASTER_PASSWORD = 'p4word';

# 从库启动复制
start slave;

参考文献

  1. 高性能MySQL·第3版 施瓦茨、扎伊采夫、特卡琴科著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝