MySQL主从复制
华子目录
- 实验环境准备
- 修改配置文件
- 实验
- `主`开启`bin-log日志`
- 创建用于`主从复制`的`用户`
- `master配置`
- `salve配置`
- 测试
- `添加一台新的slave`,如何实现`数据的全部同步`呢
- 什么环境下`主`比较多,什么环境下`从`比较多?
- 延迟复制
- 测试
- 并行复制
- `gtid`模式
- 未启用`gtid`时
- 当激活`gtid`后
- 设置`gtid`
- 测试
- 主从复制的原理
- 架构
- 三个线程
- 主从复制三步骤
- 具体操作
- 架构缺陷
- `半同步`模式
- 原理
- 启用`半同步模式`
- 什么时候我们需要`多个slave`
- 总结
- `show master status`显示信息中的`position`字段
实验环境准备
node1
为主
,node2
为从
修改配置文件
node1
上
[root@mysql-node1 ~]# vim /etc/my.cnf
服务器ID
(对于主从复制环境
,每个服务器
都需要一个唯一的ID
)
node2
上
[root@mysql-node2 ~]# vim /etc/my.cnf
服务器ID
(对于主从复制环境
,每个服务器
都需要一个唯一的ID
)
- 重启
mysql
[root@mysql-node1 ~]# /etc/init.d/mysqld restart
Shutting down MySQL... SUCCESS!
Starting MySQL. SUCCESS!
[root@mysql-node2 ~]# /etc/init.d/mysql.server restart
Shutting down MySQL.. SUCCESS!
Starting MySQL. SUCCESS!
- 验证
标识变量
#-e指定sql语句
[root@mysql-node1 ~]# mysql -uroot -p123456 -e "select @@server_id";
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
| 10 |
+-------------+
[root@mysql-node2 ~]# mysql -uroot -p123456 -e "select @@server_id";
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| @@server_id |
+-------------+
| 20 |
+-------------+
实验
主
开启bin-log日志
主
开启二进制日志
(bin-log日志
会记录所有更改数据
的语句
(insert、update、delete
等),不记录查询语句
)从
不开启二进制日志
功能
[root@mysql-node1 ~]# vim /etc/my.cnf
重启mysql
[root@mysql-node1 ~]# /etc/init.d/mysqld restart
Shutting down MySQL. SUCCESS!
Starting MySQL. SUCCESS!
- 查看
二进制日志
在主机上
的位置
[root@mysql-node1 ~]# cd /data/mysql/
- 使用
mysqlbinlog
查看二进制日志
的内容
[root@mysql-node1 mysql]# mysqlbinlog mysql-bin.000001
创建用于主从复制
的用户
master配置
[root@mysql-node1 ~]# mysql -uroot -p
Enter password:#生成专门用来做复制的用户,此用户是用于slave端做认证用的
mysql> create user huazi@"%" identified by "123456";#给他主从复制的权限
mysql> grant replication slave on *.* to huazi@"%";
#查看master的状态
mysql> show master status\G;
*************************** 1. row ***************************File: mysql-bin.000001Position: 481Binlog_Do_DB:Binlog_Ignore_DB:
Executed_Gtid_Set:
salve配置
[root@mysql-node2 ~]# mysql -uroot -p
Enter password:mysql> change master to master_host='172.25.254.10', -> master_user='huazi', -> master_password='123456', -> master_log_file='mysql-bin.000001', -> master_log_pos=481;
mysql> start slave;
mysql> show slave status\G;
测试
- 在
master
上建立一个db_hua库
mysql> create database db_hua;
- 在
slave
上查看
,发现从
上实现了与主
的同步
#我们发现从上出现了db_hua这个库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db_hua |
| mysql |
| performance_schema |
| sys |
+--------------------+
- 在
master
上建立一个表
,并插入数据
mysql> use db_hua;mysql> create table userlist(-> name varchar(10) not null,-> password varchar(20) not null-> );mysql> insert into userlist values-> ("huazi","123456");
- 在
slave
上查看,发现实现了数据同步
mysql> use db_hua;mysql> show tables;
+------------------+
| Tables_in_db_hua |
+------------------+
| userlist |
+------------------+mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
+-------+----------+
- 在
slave
上插入数据
mysql> insert into userlist values-> ("hua","789");mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 789 |
+-------+----------+
- 在
master
上查看,发现数据
没有同步过来
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
+-------+----------+
slave
上不能插入数据
,如果插入数据
后,master
上同步不到
- 回收
mysql> delete from userlist where name="hua";
添加一台新的slave
,如何实现数据的全部同步
呢
- 准备一台机子
mysql-node3
,ip
:172.25.254.30
- 修改
配置文件
,设置server-id
为30
[root@mysql-node3 ~]# cat /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=30
master
中对db_hua
中的userlist表
进行锁表
,防止在备份的过程中
不断有数据写入
,保证备份前后数据
的一致性
#锁表
mysql> flush tables with read lock;
#我们发现,此时不能插入数据
mysql> insert into userlist values-> ("hua","123");
ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock
- 使用
mysqldump命令
从master
节点上备份数据
[root@mysql-node1 ~]# mysqldump -uroot --password=123456 db_hua userlist > back1.sql
- 将
back1.sql
复制到node3主机
中
[root@mysql-node1 ~]# rsync back1.sql root@172.25.254.30:/root/
- 在
node3
上通过非交互式
建库
[root@mysql-node3 ~]# mysql -uroot -p123456 -e "create database db_hua;"
- 在
node3
上通过非交互式
导入back1.sql
文件
[root@mysql-node3 ~]# mysql -uroot -p123456 db_hua < back1.sql
- 进入
node3
的数据库
,发现已经导入成功
[root@mysql-node3 ~]# mysql -uroot -p123456
mysql> use db_hua;mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
+-------+----------+
- 在
master
上查看状态
mysql> show master status\G;
*************************** 1. row ***************************File: mysql-bin.000002Position: 154Binlog_Do_DB:Binlog_Ignore_DB:
Executed_Gtid_Set:
node3
上加入主从集群
mysql> change master to master_host='172.25.254.10',master_user='huazi',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=154;mysql> start slave;
- 查看
node3
上的slave状态
mysql> show slave status\G;
master
上解锁
mysql> unlock tables;
master
上插入数据
mysql> insert into userlist values -> ("hua","123");mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 123 |
+-------+----------+
- 在
node3
上查看,发现数据已经同步过来
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 | #原数据
| hua | 123 | #新数据
+-------+----------+
- 在
node2
上查看,发现数据正常同步
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 123 |
+-------+----------+
至此,就实现了一主两从
什么环境下主
比较多,什么环境下从
比较多?
- 有
大量数据
需要写入
时,主
比较多。有大量数据
需要读取
时,从
比较多
延迟复制
延迟复制
是用来控制sql线程
的,和i/o线程
无关延迟复制
不是i/o线程
过段时间来复制
,i/o
是正常工作
的- 是
日志
已经保存在slave端
了,那个sql
要等多久进行回放
- 设置
延迟
是为了防止主
的误操作
在node2
上设置延迟
#先关闭sql线程
mysql> stop slave sql_thread;#设置延迟时间为60秒
mysql> change master to master_delay=60;#再开启sql线程
mysql> start slave sql_thread;
#我们发现时间已经改为了60秒
#即:每次主上修改数据后,从要等上60秒再进行数据同步
mysql> show slave status\G;
测试
- 在
master
上不小心删除
了一条数据
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 123 |
+-------+----------+mysql> delete from userlist where name="hua";
60秒
之前,node2
上
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 123 |
+-------+----------+#立即将数据导出
[root@mysql-node2 ~]# mysqldump -uroot --password=123456 db_hua userlist > back2.sql
60秒
之后,node2
上
mysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
+-------+----------+
master
上做数据恢复
[root@mysql-node2 ~]# rsync back2.sql root@172.25.254.10:/root/[root@mysql-node1 ~]# mysql -uroot -p123456 db_hua < back2.sqlmysql> select * from userlist;
+-------+----------+
| name | password |
+-------+----------+
| huazi | 123456 |
| hua | 123 |
+-------+----------+
数据恢复
后,slave
上也会进行数据同步
并行复制
- 查看
slave
中的线程信息
mysql> show processlist;
+----+-------------+-----------+--------+---------+-------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+--------+---------+-------+--------------------------------------------------------+------------------+
| 5 | root | localhost | db_hua | Sleep | 52137 | | NULL |
| 6 | system user | | NULL | Connect | 54465 | Waiting for master to send event | NULL |
| 7 | system user | | NULL | Connect | 53053 | Slave has read all relay log; waiting for more updates | NULL |
| 8 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------------+-----------+--------+---------+-------+--------------------------------------------------------+------------------+
- 默认情况下
slave
中使用的是sql单线程回放
复制master
数据的 - 在
master
中多用户读写
,如果使用sql单线程回放
,那么就会造成组从复制延迟严重
- 开启
MySQL
的多线程回放
可以解决上述问题
注意:如果
slave
上有延迟时间
的设置
,则不设置多线程回放
多线程回放
是在slave
中设置的
#在node3上
[root@mysql-node3 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=30gtid_mode=on
enforce-gtid-consistency=on
slave-parallel-type=LOGICAL_CLOCK #基于组提交
slave-parallel-workers=16 #开启线程数量
master_info_repository=TABLE #master信息在表中记录,默认记录
relay_log_info_repository=TABLE #回放日志信息在表中记录,默认记录
relay_log_recovery=ON #日志回放恢复功能开启
#重启
[root@mysql-node3 ~]# /etc/init.d/mysqld restart[root@mysql-node3 ~]# mysql -uroot -p
Enter password:mysql> show processlist;
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
| 2 | system user | | NULL | Connect | 18 | Slave has read all relay log; waiting for more updates | NULL |
| 3 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 4 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 6 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 7 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 8 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 9 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 10 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 11 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 12 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 13 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 14 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 15 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 16 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 17 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 18 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 19 | system user | | NULL | Connect | 18 | Waiting for an event from Coordinator | NULL |
| 20 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------------+-----------+------+---------+------+--------------------------------------------------------+------------------+
- 此时
sql线程
转化为协调线程
,16
个worker
负责处理sql协调线程
发送过来的处理请求
gtid
模式
未启用gtid
时
- 在
master端
的写入
时多用户读写
,在slave端
复制时单线程日志回放
,所以slave端
一定会延迟
- 这种
延迟
在slave端
的延迟
可能会导致数据同步不一致
,当master挂掉
后slave接管
,一般会挑选
一个和master延迟日志
最接近
的充当
新的master
- 那么
未接管master
的主机
继续充当slave角色
并会指向
到新的master
上,作为其slave
- 这时候
按照之前
的配置
我们需要知道新的master
上的pos
的id
,但是我们无法确定
新的master
和slave之间差多少
当激活gtid
后
- 当
master出现问题
后,slave2
和master
的数据最接近
,会被作为新的master
slave1
指向新的master
,但是slave1
不会去检测新的master
的pos id
,只需要继续
读取自己gtid_next
即可
设置gtid
master端
和slave端
都开启gtid
#master上
[root@mysql-node1 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=10
log-bin=mysql-bin
gtid_mode=on
enforce-gtid-consistency=on
#slave上
[root@mysql-node2 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=20
gtid_mode=on
enforce-gtid-consistency=on
#slave上
[root@mysql-node3 ~]# vim /etc/my.cnf
[mysqld]
datadir=/data/mysql
socket=/data/mysql/mysql.sock
symbolic-links=0
server-id=30
gtid_mode=on
enforce-gtid-consistency=on
- 重启
mysql
[root@mysql-node1 ~]# /etc/init.d/mysqld restart[root@mysql-node2 ~]# /etc/init.d/mysql.server restart[root@mysql-node3 ~]# /etc/init.d/mysqld restart
- 检测
是否开启
[root@mysql-node1 ~]# mysql -uroot -p123456 -e "select @@session.gtid_next;"
+---------------------+
| @@session.gtid_next |
+---------------------+
| AUTOMATIC | #开启
+---------------------+[root@mysql-node2 ~]# mysql -uroot -p123456 -e "select @@session.gtid_next;"
+---------------------+
| @@session.gtid_next |
+---------------------+
| AUTOMATIC |
+---------------------+[root@mysql-node3 ~]# mysql -uroot -p123456 -e "select @@session.gtid_next;"
+---------------------+
| @@session.gtid_next |
+---------------------+
| AUTOMATIC |
+---------------------+
slave端设置
[root@mysql-node2 ~]# mysql -uroot -p
Enter password:
#先停掉slave
mysql> stop slave;mysql> change master to-> master_host="172.25.254.10",-> master_user='huazi',-> master_password="123456",-> master_auto_position=1;#再开启
mysql> start slave;
[root@mysql-node3 ~]# mysql -uroot -p
Enter password:#先停掉
mysql> stop slave;mysql> change master to-> master_host="172.25.254.10",-> master_user="huazi",-> master_password="123456",-> master_auto_position=1;#再开启
mysql> start slave;
测试
#master上
mysql> show master status\G;
*************************** 1. row ***************************File: mysql-bin.000003Position: 154Binlog_Do_DB:Binlog_Ignore_DB:
Executed_Gtid_Set:
#slave上(node2)
mysql> show slave status\G;
#slave上(node3)
mysql> show slave status\G;
主从复制的原理
架构
三个线程
实际上主从同步
的原理
就是基于bin-log日志
进行数据同步
的。在主从复制
过程中,会基于3个线程
来操作,一个主库线程
,两个从库线程
。
二进制日志转储线程
(Binlog dump thread
)是一个主库线程
。当从库线程
连接的时候,主库线程
可以将bin-log日志
发送给从库I/O线程
,当主库读取事件
(Event
)的时候,会在Bin-log
上加锁
,读取完成之后
,再将锁释放掉
从库I/O线程
会连接到主库
,向主库
发送请求
更新Bin-log
。这时从库的I/O线程
就可以读取
到主库
的二进制日志转储线程
发送的Bin-log
更新部分,并且拷贝
到本地
的中继日志
(Relay log
)从库SQL线程
会读取从库
中的中继日志
,并且执行日志
中的事件
,将从库
中的数据
与主库
保持同步
主从复制三步骤
步骤1
:Master
将修改操作
记录到二进制日志
(bin-log
)步骤2
:Slave
将Master
的binary log
拷贝到它的中继日志
(relay log
)步骤3
:Slave
重做中继日志
中的事件
,将改变应用
到自己的数据库
中。MySQL
复制是异步且串行化的
,而且重启
后从接入点
开始复制
具体操作
slave端
中设置了master端
的ip
,用户名
,用户密码
、日志文件名
,和日志的Position
,通过这些信息
取得master
的认证及信息
master端
在设定好bin-log
启动后会开启binlog dump
的线程
master端
的binlog dump线程
把二进制
的更新
发送到slave端
slave端
开启两个线程
,一个是I/O线程
,一个是sql线程
i/o线程
用于接收master端
的二进制日志
,此线程
会在本地
打开relay-log中继日志
,并且保存
到本地磁盘
sql线程
读取本地relog中继日志
进行回放
架构缺陷
主从架构
采用的是异步机制
master
更新完成后直接
发送二进制日志
到slave
,但是slave
是否真正保存了数据
,master端
不会检测
master端
直接保存二进制日志
到磁盘
- 当
master端
到slave端
的网络出现问题
或者master端
直接挂掉
,二进制日志
可能根本没有到达slave
master
出现问题后,slave端
接管master
,这个过程中数据就丢失了
这样的问题出现
就无法达到数据
的强一致性
,零数据丢失
半同步
模式
原理
用户线程写入完成
后master
中的dump线程
会把二进制日志
推送到slave端
slave
中的i/o线程
接收后保存
到relay-log中继日志
保存完成
后slave
向master端
返回ack
进行确认- 在
未接受
到slave
的ack
时,master端
是不做提交的
,一直处于等待
,当收到ack
后提交
到存储引擎
- 在
5.6版本
中用到的是after_commit模式
,after_commit模式
是先提交再等待ack返回
后输出ok
启用半同步模式
- 做
半同步之前
需要开启gtid
什么时候我们需要多个slave
- 当
读取操作
远远高于写操作
时。我们采用一主多从
架构 - 数据库
外层接入负载均衡层
并搭配高可用机制
总结
show master status
显示信息中的position
字段
在MySQL
中,SHOW MASTER STATUS
命令的输出包含了关于当前主服务器
(Master
)二进制日志
(Binary Log
)状态的信息
。其中,Position
字段提供了关于二进制日志文件
中当前写入位置
的关键信息
。
- 具体来说,
Position
字段表示的是在当前二进制日志文件
中,下一个
即将被写入的事件的起始字节偏移量
。这个偏移量
是从二进制日志文件
的开头
算起的。每当主服务器
执行一个会改变数据库状态
的操作
(如INSERT
、UPDATE
或DELETE
语句)时,这个操作
会被记录为一个事件
,并追加
到当前的二进制日志文件
中。Position
字段的值
会随着新事件
的写入
而递增
- 在
复制
(Replication
)环境
中,从服务器
(Slave
)会使用这个Position
值来确定它应该从主服务器
的二进制日志文件
的哪个位置
开始读取事件
。这样,从服务器
就能够准确
地复制主服务器上
的所有更改
。
举个例子,如果 SHOW MASTER STATUS
命令的输出如下:
+------------------+----------+--------------+------------------+---------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+---------------------------------------+
| mysql-bin.000001 | 1543 | | | |
+------------------+----------+--------------+------------------+---------------------------------------+
- 在这个例子中,
Position
字段的值是1543
。这意味着在当前
的二进制日志文件mysql-bin.000001
中,下一个事件
将从文件
的第1543
个字节开始写入
(实际上,由于字节偏移量
是从0
开始的,所以第1543
个字节
是文件中
的第1544
个位置,但通常我们按照偏移量
来理解这个值) - 如果设置了
复制
,从服务器
将会从这个位置
开始读取事件
,以确保
它能够复制主服务器
上的所有最新更改