MySQL主从复制
在之前“复制”文章中提到过,复制解决的基本问题是让分布式服务器中的数据保持同步。主节点的数据可以同步到从节点上,同时当主节点发生故障时,从节点也可以被切换为主节点。
对于MySQL复制,支持基于行的复制和基于语句的复制方式。而这几种方式都是通过在主库上记录二进制日志、在备库重放日志的方式来实现异步的数据复制。
首先mysql复制是如何工作的呢?
总的来说,mysql复制有三个步骤:
- 1.在主库上把数据更改记录到二进制日志(Binary Log,二进制日志事件)中;
在每次准备提交事务完成数据更新前,主库将数据更新的时间记录到二进制日志中。mysql会按照事务提交的顺序,而非每条语句的执行顺序来记录二进制日志。在记录二进制日志后,主库会告诉存储引擎可以提交事务。
- 2.从库将主库上的日志复制到自己的中继日志(relay log)中;
首先,从库会启动一个工作线程-I/O线程,I/O线程与主库建立一个普通的客户端连接;
然后在主库上会启动一个二进制转储(binlog dump)线程,该二进制转储线程会读取主库的二进制日志中的事件,返回给从库I/O线程,从库I/O线程会将接收到的事件记录到中继日志中。
该二进制转储线程不会对事件进行轮询。当该线程追赶上主库时,它将进入睡眠状态,直到主库发送信号量通知其有新的事件产生时才会被唤醒。
- 3.从库读取中继日志中的事件,并将其重放到从库数据之上。
从库的sql线程执行最后写入过程。该sql线程从中继日志中读取事件并在从库执行,从而实现从库数据的更新。
另外,可以通过配置选项来决定,在sql线程执行事件时,是否写入其自己的二进制日志中。
该复制架构实现了读取事件和重放事件的解耦,允许两个过程异步进行。I/O线程和重放sql线程是可以相互独立工作的。
下图描述的就是mysql复制的流程:
主从复制配置实战
下面我以docker系统安装MySQL 5.7的主从同步来演示主从同步过程。
- 拉取mysql镜像
$ docker pull mysql:5.7
- 创建mysql在宿主机对应的目录映射
$ mkdir -p /usr/local/mysql57-master/{logs,data}
$ mkdir -p /usr/local/mysql57-slave/{logs,data}
- 启动mysql容器(这里分别用3306和3316作为主从端口)
$ docker run -d --name mysql57-master \
-v ./usr/local/mysql57-master:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root@123 \
-p 3306:3306 \
mysql:5.7
$ docker run -d --name mysql57-slave \
-v ./usr/local/mysql57-slave:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root@123 \
-p 3316:3306 \
mysql:5.7
- 配置master节点
# 进入容器
$ docker exec -it mysql57-master /bin/bash
# 修改my.cnf配置,开启日志功能、配置server-id
[mysqld]
## 同一局域网内注意要唯一
server-id=100
## 开启二进制日志功能
log-bin=mysql-bin
# 重启数据库,使配置生效,并重新启动容器
$ service mysql restart
$ docker start mysql57-master
# 再次进入master节点容器:创建数据同步用户,并授予该用户 REPLICATION SLAVE权限和REPLICATION CLIENT权限,用于在主从间同步数据
> CREATE USER 'slave01'@'%' IDENTIFIED BY '1234567';
> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave01'@'%';
- 配置slave节点
# 进入容器
$ docker exec -it mysql57-slave /bin/bash
# 修改my.cnf配置,开启日志功能、配置server-id
[mysqld]
## 设置server_id,注意要唯一
server-id=101
## 开启二进制日志功能,以备Slave作为其它Slave的Master时使用
log-bin=mysql-slave-bin
## relay_log配置中继日志
relay_log=edu-mysql-relay-bin
#复制的数据写入日志中
log_slave_updates=1
# 重启数据库,使配置生效,并重新启动容器
$ service mysql restart
$ docker start mysql57-slave
- 连接master和slave节点
slave节点
> change master to master_host='172.17.0.2', master_user='slave01', master_password='1234567', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos=0;
# 开始复制
> start slave
# 查看主从同步状态
show slave status \G;
在未同步情况下,SlaveIORunning 和 SlaveSQLRunning 都是No,因为我们还没有开启主从复制过程。
使用start slave开启主从复制过程,然后再次查询主从同步状态(show slave status),SlaveIORunning 和 SlaveSQLRunning 应该都是YES。
另外,通过 show processlist 可以查看到同步中所用到的三个线程:
# 主节点上由从库I/O线程发起的连接
mysql> show processlist \G
*************************** 1. row ***************************
Id: 17
User: slave01
Host: 172.17.0.3:41806
db: NULL
Command: Binlog Dump
Time: 12573
State: Master has sent all binlog to slave; waiting for more updates
Info: NULL
# 从节点的两个线程
mysql> show processlist \G
*************************** 2. row ***************************
Id: 30
User: system user
Host:
db: NULL
Command: Connect
Time: 12489
State: Waiting for master to send event
Info: NULL
*************************** 3. row ***************************
Id: 31
User: system user
Host:
db: NULL
Command: Connect
Time: 12102
State: Slave has read all relay log; waiting for the slave I/O thread to update it
Info: NULL
下次我们会继续以mysql为例来详细讲解复制的几种实现方式:基于语句复制、基于行复制,并分析这两种复制方式的优缺点。
更多分布式、区块链技术分享,请关注公众号【伟达实验室】