纸上得来终觉浅,绝知此事要躬行。
主从复制
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000001', master_log_pos= 1547, master_connect_retry=30;
纸上得来终觉浅,绝知此事要躬行。
概述
复制是指将主数据库的DDL
和 DML
操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。
MySQL
支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。
优势
MySQL
复制的优点主要包含以下三个方面:
-
主库出现问题,可以快速切换到从库提供服务。
-
可以在从库上执行查询操作,从主库中更新,实现读写分离,降低主库的访问压力。
-
可以在从库中执行备份,以避免备份期间影响主库的服务。
原理
在详细介绍如何设置复制之前,我们先看看MySQL
实际上是如何复制数据的,总的来说,复制分为三个步骤:
- 在主库上把数据更改记录到二进制日志(Binary Log)中。
- 备库将主库上的日志复制到自己的中继日志(Relay Log)中。
- 备库重做中继日志中的日志,把更改应用到自己的数据库上,已达到数据的一致性。
拆分这三步,具体过程如下:
第一步在主库上记录二进制文件。在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中。MySQL
会按照事务提交的顺序而非每条语句的执行顺序来记录二进制日志。在记录二进制日志之后主库会告诉存储引擎提交事务。即:在事务提交之前记录二进制日志
第二步,从库将主库的二进制日志复制到其本地的中继日志。首先从库会启动一个工作线程称为I/O线程,该线程跟主库建立一个普通的客户端连接,然后在主库上启动一个特殊的二进制转储(binlog dump
)线程,这个二进制转储线程会读取主库上二进制日志事件,他不会对事件进行轮询。如果该线程追赶上了主库,他将进入睡眠状态,直到主库发送信号量通知从库有新的事件才会被唤醒,然后线程将接受到的事件记录到中继日志。
第三步,从库中的线程从中继日志中读取事件并在从库执行,从而实现从库数据的更新。
配置
Master配置
- 查看
MySQL
默认配置文件
mysql --help|grep 'my.cnf'
- 选择一个配置文件输入以下配置信息
# 必须,表示mysql配置文件
[mysqld]
# mysql 服务ID,保证整个集群环境中唯一
server-id=1
# mysql binlog 日志的存储路径和文件名
log-bin=/var/lib/mysql/mysqlbin
# 错误日志,默认已经开启
# log-err
# mysql的安装目录
# basedir
# mysql的临时目录
# tmpdir
# mysql的数据存放目录
# datadir
# 是否只读,1 代表只读, 0 代表读写
read-only=0
# 忽略的数据, 指不需要同步的数据库
binlog-ignore-db=mysql
# 指定同步的数据库
# binlog-do-db=db01
- 保存完毕之后,重启服务
service mysql restart
- 创建同步数据的账户,并且进行授权操作
grant replication slave on *.* to '从库用户名'@'从库服务器地地址' identified by '密码';
# 例如:grant replication slave on *.* to 'root'@'192.168.192.131' identified by 'root';
flush privileges;
- 查看log-bin是否已打开
mysql> show variables like '%log_bin%';
+---------------------------------+-------------------------------+
| Variable_name | Value |
+---------------------------------+-------------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/mysqlbin |
| log_bin_index | /var/lib/mysql/mysqlbin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+-------------------------------+
6 rows in set (0.00 sec)
- 查看master状态
mysql> show master status;
+-----------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------+----------+--------------+------------------+-------------------+
| mysqlbin.000001 | 154 | | mysql | |
+-----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
# File : 从哪个日志文件开始推送日志文件
# Position : 从哪个位置开始推送日志
# Binlog_Ignore_DB : 指定不需要同步的数据库
Slave配置
- 与Master一样查找配置文件,输入以下内容
# mysql服务端ID,唯一
server-id=2
# 指定binlog日志
log-bin=/var/lib/mysql/mysqlbin
- 保存完毕之后,重启服务
service mysql restart
- 登录数据库,输入以下命令
change master to master_host= '192.168.192.130', master_user='主库用户名', master_password='主库密码', master_log_file='mysqlbin.000001', master_log_pos=154;
指定当前从库对应的主库的IP地址,用户名,密码,从哪个日志文件开始的那个位置开始同步推送日志。
- 开启同步操作
start slave;
show slave status;
- 停止同步操作
stop slave;
测试配置同步
- 在主库中创建数据库,创建表,并插入数据 :
create database db01;
user db01;
create table user(
id int(11) not null auto_increment,
name varchar(50) not null,
sex varchar(1),
primary key (id)
)engine=innodb default charset=utf8;
insert into user(id,name,sex) values(null,'Tom','1');
insert into user(id,name,sex) values(null,'Trigger','0');
insert into user(id,name,sex) values(null,'Dawn','1');
- 在从库中查询数据,进行验证 :
在从库中,可以查看到刚才创建的数据库:
在该数据库中,查询user表中的数据:
基于Docker搭建
本次通过Docker搭建是利用Docker开启多个MySQL
实例进行搭建,不需要额外的虚拟机,配置方便。服务分配如下:
服务器 | 端口 | 启动 |
---|---|---|
172.17.0.2 | 5566 | mysql-master |
172.17.0.3 | 5577 | mysql-slave1 |
172.17.0.4 | 5588 | mysql-slave2 |
主服务器搭建
1.首先在172.17.0.2拉取docker镜像,我们这里使用5.7版本的MySQL
:
$ docker pull mysql:5.7
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/mysql 5.7 e1e1680ac726 7 months ago 373 MB
2.创建一个目录,用于宿主机与Docker容器建立数据卷
$ mkdir -p /data/docker/mysql-master/
3.在当前目录中创建配置文件my.cnf
,次文件要映射到MySQL
的docker容器中的配置文件,文件中加入内容如下:
[mysqld]
skip-name-resolve
server-id=1
log-bin=mysql-bin
binlog-do-db=tx_db
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
4.创建主机容器,Master对外映射的端口是5566
$ docker run -p 5566:3306 --name mysql-master -v /data/docker/mysql-master:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
5.查看容器运行情况
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d74def70b04 mysql:5.7 "docker-entrypoint.s…" 2 hours ago Up 2 hours 33060/tcp, 0.0.0.0:5566->3306/tcp mysql-master
6.在Master数据库创建数据同步用户
CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
flush privileges;
从服务器搭建
1.创建目录,用于宿主机与Docker容器建立数据卷
$ mkdir -p /data/docker/mysql-slave1/
$ mkdir -p /data/docker/mysql-slave2/
2.分别在两个目录中创建配置文件my.cnf
[mysqld]
skip-name-resolve
server-id= 2
log-bin=mysql-bin
replicate-do-db=tx_db
binlog-ignore-db = mysql
binlog-ignore-db = information_schema
3.创建主机容器,Slave1
对外映射的端口是5577,Slave2
对外映射的端口是5588
$ docker run -p 5577:3306 --name mysql-slave1 -v /data/docker/mysql/mysql-slave1:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
$ docker run -p 5588:3306 --name mysql-slave2 -v /data/docker/mysql/mysql-slave2:/etc/mysql/mysql.conf.d -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
4.查看容器运行情况
$ docker ps -a
5.登录从服务器
docker exec -it [CONTAINER ID] /bin/bash
6.查看主服务器状态
mysql> show master status;
+------------------+----------+--------------+--------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin.000002 | 154 | tx_db | mysql,information_schema | |
+------------------+----------+--------------+--------------------------+-------------------+
1 row in set (0.00 sec)
7.登录数据库,输入以下命令
方式一:容器内网访问
master_host
可以通过docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器名称|容器id
查询。端口是3306
change master to master_host='172.17.0.2', master_user='slave', master_password='123456', master_port=3306, master_log_file='mysql-bin.000002', master_log_pos= 154, master_connect_retry=30;
方式二:映射宿主机访问,master_host
是宿主机IP
,端口是对外映射端口
change master to master_host='192.168.0.108', master_user='slave', master_password='123456', master_port=5566, master_log_file='mysql-bin.000002', master_log_pos= 154, master_connect_retry=30;
master_host
:Master的地址,指的是容器的独立IP
master_port
:Master的端口号,指的是容器的端口号master_user
:用于数据同步的用户master_password
:用于同步的用户的密码master_log_file
:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值master_log_pos
:从哪个 Position 开始读,即上文中提到的 Position 字段的值master_connect_retry
:如果连接失败,重试的时间间隔,单位是秒,默认是60秒
8.开启同步操作
- 方式一连接登录:
mysql> start slave;
mysql> show slave statusG
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.17.0.2 --注意IP
Master_User: slave
Master_Port: 3306 --注意端口
Connect_Retry: 30
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 154
Relay_Log_File: 2a42f09f6e2b-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes --IO线程启动
Slave_SQL_Running: Yes --SQL线程启动
Replicate_Do_DB: tx_db --需要同步的数据库
..............
- 方式二连接登录:
mysql> start slave;
mysql> show slave statusG
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.0.108 --注意IP
Master_User: slave
Master_Port: 5566 --注意端口
Connect_Retry: 30
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 154
Relay_Log_File: 2a42f09f6e2b-relay-bin.000002
Relay_Log_Pos: 320
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes --IO线程启动
Slave_SQL_Running: Yes --SQL线程启动
Replicate_Do_DB: tx_db --需要同步的数据库
................
9.关闭同步
stop slave;
测试
1.主服务器建库建表,添加数据
mysql> create database tx_db default character set=utf8;
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| tx_db |
+--------------------+
5 rows in set (0.00 sec)
mysql> use tx_db;
mysql> create table user(id int,name varchar(20));
Query OK, 0 rows affected (0.50 sec)
2.从服务器查询数据
mysql> select * from user;
+------+--------+
| id | name |
+------+--------+
| 1 | ydongy |
+------+--------+
1 row in set (0.00 sec)