1、主从复制介绍
- 主从复制是基于binlog来实现的
- 主库发生新的操作,都会记录binlog
- 从库取得主库的binlog进行回放
- 主从复制的过程是异步操作
2、主从复制的前提
- 需要2个或以上的数据库实例
- 主库需要开启二进制日志
- server_id要不同,区分不同的节点
- 主库需要建立专用的复制用户 (replication slave)
- 从库应该通过备份主库,然后恢复自身数据
- 创建从库的同步信息(ip、port、user、pass、二进制日志起点)
- 从库应该开启专门的复制线程
3、搭建主从复制环境
-
创建多实例
-
检查配置文件
- 主库:二进制日志是否开启
- 两个节点: server_id(建议主库ID号小于从库ID号)
-
主库创建复制用户
# 创建用户 mysql> create user rep@'192.168.137.%' identified by '123'; # 给用户授权 mysql> grant replication slave on *.* to rep@'192.168.137.%';
-
备份主库数据并导入从库
# 主库全量备份 root@localhost data]# mysqldump -S /data/db_1/data/mysql.sock -A --master-data=2 --single-transaction -R -E --triggers >/tmp/full.sql # 从库导入数据 [root@localhost data]# /data/db_1/data/mysql.sock mysql> set sql_log_bin=0; mysql> source /tmp/full.sql
-
创建从库的同步信息
mysql> CHANGE MASTER TO MASTER_HOST='192.168.137.10', MASTER_USER='rep', MASTER_PASSWORD='123', MASTER_PORT=3307, MASTER_LOG_FILE='bin_log.000002', MASTER_LOG_POS=156, MASTER_CONNECT_RETRY=10; # 重新配置'CHANGE MASTER TO',需要先停止slave,然后重置slave,然后再重新配置 mysql> stop slave; mysql> reset slave;
-
从库开启复制线程(IO,SQL)
[root@localhost data]# mysql -S /data/db_2/mysql.sock mysql> start slave;
-
检查主从复制状态
# 在从库中操作 mysql> show slave status \G # 或者在主库中执行一些操作,如果从库中有相应变化,说明主从复制搭建正常
4、主从复制原理
4.1、主从复制涉及的文件
-
主库
binlog:二进制日志
-
从库:
relaylog:中继(回放)日志
master.info :主库信息文件
relaylog.info:relaylog应用的信息
4.2、主从复制涉及的线程
- 主库
Binlog_Dump Thread(DUMP线程) - 从库:
SLAVE_IO_THREAD(I/O线程)
SLAVE_SQL_THREAD(SQL线程)
4.3、主从复制工作(过程)原理
-
从库执行
change master to
命令(主库的连接信息+复制的起点) -
从库会将以上信息,记录到master.info文件
-
从库执行
start slave
命令,立即开启IO线程和SQL线程 -
从库 IO线程读取master.info文件中的信息,获取到IP、PORT、User、Password、binlog的位置信息
-
从库IO线程请求连接主库,主库专门提供一个DUMP线程,负责和IO线程交互
-
IO线程根据binlog的位置信息(bin_log.000002 , 156),请求主库新的binlog
-
主库通过DUMP线程将最新的binlog,通过网络传送给从库的IO线程
-
IO线程接收到新的binlog日志,存储到TCP/IP缓存,立即返回ACK给主库,并更新master.info
-
IO线程将TCP/IP缓存中的数据转储到磁盘relaylog中
-
SQL线程读取relay.info中的信息,获取到上次已经应用过的relaylog的位置信息
-
SQL线程会按照上次的位置点回放最新的relaylog,再次更新relay.info信息
-
从库会自动purge应用过的relaylog进行定期清理
说明:
一旦主从复制构建成功,主库当中发生了新的变化,都会通过dump线程发送信号给IO线程,该功能增强了主从复制的实时性
4.4、主从复制监控
-
监控命令
在从库中执行
# 查看从库状态 mysql> show slave status \G # 查看从库回放日志 mysql> show relaylog events in LOG_FILE;
-
输出信息解析
# 主库有关的信息(master.info) Master_Host: 192.168.137.10 Master_User: rep Master_Port: 3307 Connect_Retry: 10 Master_Log_File: bin_log.000003 Read_Master_Log_Pos: 976 # 从库relay应用信息有关的(relay.info) Relay_Log_File: localhost-relay-bin.000004 Relay_Log_Pos: 1187 Relay_Master_Log_File: bin_log.000003 # 从库线程运行状态(排错) Slave_IO_Running: Yes Slave_SQL_Running: Yes Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: # 过滤复制有关的信息 Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: # 从库延时主库的时间(秒) Seconds_Behind_Master: 0 # 设置从库同步延时 SQL_Delay: 0 SQL_Remaining_Delay: NULL # GTID复制有关的状态信息 Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 0
4.5、主从复制故障
4.5.1、IO线程故障
-
连接主库:connecting
-
故障原因
网络故障、连接信息错误或变更、防火墙、连接数达到上限
-
排查思路:
- 使用复制用户手工登录,查看具体错误信息
-
解决方法
mysql> stop slave; mysql> reset slave all; # 修改“change master to”参数 mysql> change master to mysql> start slave;
-
-
请求:Binlog
-
故障原因
-
binlog 没开、损坏、不存在
-
主库执行了
reset master
-
-
解决方法
在从库中执行以下操作
# 停止slave mysql> stop slave ; # 重置slave mysql> reset slave all; # 重做CHANGE MASTER TO mysql> CHANGE MASTER TO MASTER_HOST='192.168.137.10', MASTER_USER='rep', MASTER_PASSWORD='123', MASTER_PORT=3307, MASTER_LOG_FILE='bin_log.000007', MASTER_LOG_POS=1633, MASTER_CONNECT_RETRY=10; # 启动slave mysql> start slave;
-
4.5.2、SQL线程故障
SQL线程主要是完成relaylog回放,所以处理SQL线程故障时,主要是做SQL语句执行失败原因的分析
-
故障原因
可能是操作失误,导致从库的数据比主库的数据新,造成数据冲突或异常
-
解决方法
- 一切以主库为准进行解决
- 如果出现故障,尽量将从库进行反操作
- 最直接、稳妥的办法,重新构建主从复制
-
主键冲突的解决办法
查找到主\从库之间主键冲突数据,然后将从库中的数据update一下,然后跳过这一条错误
mysql> stop slave; mysql> set global sql_slave_skip_counter = [events个数]; mysql> start slave;
可以使用pt-table-check校验两个表的差异,然后使用pt-table-sync进行同步,然后跳过错误,进行主从复制
-
常见错误代码
- 1007:对象已存在
- 1032:无法执行DML
- 1062:主键冲突,或约束冲突
-
避免SQL线程故障
-
从库设置只读
在配置文件中添加以下参数
read_only = ON super_read_only = ON
-
使用读写分离中间件
-
4.6、主从延时原因及监控
4.6.1、主库方面原因与解决方法
-
binlog写入不及时
在配置文件中加入
sync_binlog=1
参数 -
默认情况下dump线程是串行方式传输binlog
在并发事务量大或者大事务时,由于dump线程是串型工作的,导致传送日志较慢,因此必须开启GTID复制,使用Group commit方式,可以支持DUMP线程并行传输 -
主库极其繁忙
慢语句、锁等待、从库数量多、网络延时等
4.6.2、从库方面原因
- 传统复制(Classic)中
如果主库并发事务量很大,或者出现大事务,由于从库是单SQL线程,导致不管传的日志有多少,只能一次执行一个事务 - 主从硬件差异太大
- 主从的参数配置
- 从库和主库的索引不一致
- 版本有差异
4.6.3、主从延时监控
-
主库方面原因的监控
对比主库和从库的bin_log文件、position号是否相同
-
主库
mysql> show master status; File: mysql-bin.000001 Position: 1373
-
从库
mysql> show master status; Master_Log_File: mysql-bin.000001 Read_Master_Log_Pos: 1373
-
-
从库方面原因监控
-
查看从主库获取到多少事务
-
查看从库执行了多少事务
# 对比获取到的日志数量和回放的日志数量,以确定影响执行效率的事务;如果两个号码一致,说明没有延时 mysql> show master status; Read_Master_Log_Pos: 976 Exec_Master_Log_Pos: 976
-
4.7、从库同步延时
4.7.1、作用与配置
-
作用
设置从库同步延时,主要是为了防止操作失误或逻辑故障导致数据异常
-
执行原理
主要是修改SQL线程执行延时,数据已经正常写入relaylog中,只是SQL线程在指定延时时间后再执行
-
配置
mysql>stop slave; # 从库同步延时时间设置(单位:秒) mysql>CHANGE MASTER TO MASTER_DELAY = 300; mysql>start slave; # 查看从库同步延时时间 mysql> show slave status \G # 设置的延时时间 SQL_Delay: 300 # 剩余的延时时间 SQL_Remaining_Delay: NULL
4.7.2、使用从库同步延时,处理逻辑故障
-
从库同步延时的恢复思路
-
监控到数据库逻辑故障
-
停止从库SQL线程,记录已经回放的位置点(既:截取日志起点)
mysql> stop slave sql_thread; mysql> show slave status \G Relay_Log_File: localhost-relay-bin.000003 Relay_Log_Pos: 322
-
截取relaylog
# 起点: mysql>show slave status \G Relay_Log_File: localhost-relay-bin.000003 Relay_Log_Pos: 322 # 终点: drop之前的位置点 mysql> show relaylog events in 'localhost-relay-bin.000003'; # 进行截取
-
模拟SQL线程回放日志
从库执行source,导入数据 -
恢复业务
-
只有一个库
从库替代主库工作 -
有多个库
从库导出故障库,还原到主库中
-
-
4.7.3、故障演练
-
主库操作
mysql> create database test_db charset utf8mb4; mysql> use test_db; mysql> create table t1 (id int); mysql> insert into t1 values(1),(2),(3); mysql> commit; mysql> drop database test_db;
-
从库操作
-
停止从库SQL线程,获取relay的位置点(截取日志的起点)
mysql> stop slave sql_thread; mysql> show slave status \G Relay_Log_File: localhost-relay-bin.000014 Relay_Log_Pos: 1016
-
找到relay的截取终点
drop事务的开始点就是上一个事务的结束点,因此我们的日志只需要截取到drop事务开始号即可
mysql> show relaylog events in 'localhost-relay-bin.000014'; | localhost-relay-bin.000014 | 1093 | Query | 1 | 15290 | drop database test_db /* xid=1641 */ |
-
截取relay
[root@localhost ~]# cd /data/db_2/data/ [root@localhost data]# mysqlbinlog --start-position=1016 --stop-position=1093 localhost-relay-bin.000014 >/tmp/relay.sql
-
恢复relay到从库
[root@localhost data]# mysql -uroot -p -S /data/db_2/data/mysql.sock mysql> set sql_log_bin=0; mysql> source /tmp/relay.sql
-
恢复主库
# 在从库中备份主库损坏的数据库 [root@localhost data]# mysqldump -S /data/db_2/data/mysql.sock -B test_db --master-data=2 --single-transaction -R -E --triggers >/tmp/test_db.sql # 在主库中恢复损坏数据库 [root@localhost data]# mysql -S /data/db_1/data/mysql.sock mysql> set sql_log_bin=0; mysql> source /tmp/test_db.sql mysql> set sql_log_bin=1; # 恢复数据后,查看误操作事务数量 mysql> show binlog events in 'bin_log.000007'; # 根据实际误操作事务数量,在“从库”中跳过对应事务数量 mysql> stop slave; mysql> set global sql_slave_skip_counter = 1; mysql> start slave; # 查看所有数据同步完毕后,将“从库”中跳过事务数量为0 mysql> stop slave; mysql> set global sql_slave_skip_counter = 0; mysql> start slave; # 数据库恢复完毕
-
4.8、过滤复制应用
-
主库参数
在主库配置文件中设置日志记录黑/白名单参数,选择性记录日志
# 日志记录白名单 binlog_do_db=[DATABASE] # 日志记录黑名单 binlog_ignore_db=[DATABASE]
-
从库参数
可以在从库配置文件中设置日志记录黑/白名单参数,选择性记录日志;也可以使用“CHANGE MASTER TO”设置
# 库的黑/白名单 replicate_do_db=[DATABASE] replicate_ignore_db=[DATABASE] # 表的黑/白名单 replicate_do_table=[TABLE] replicate_ignore_table=[TABLE] # 表的黑/白名单(模糊匹配) replicate_wild_Do_table=[TABLE] replicate_wild_Ignore_table=[TABLE]
4.9、GTID复制应用
4.9.1、核心参数
# 启用gtid模式,否则就是普通的复制架构
gtid-mode=on
# 强制GTID的一致性
enforce-gtid-consistency=true
# slave更新是否记入日志
log-slave-updates=1
4.9.2、主/从库参数配置
-
主库配置
主库配置与普通主从配置相同
-
从库配置
mysql> change master to master_host='192.168.137.10', master_user='rep', master_password='123' , # GTID号从1开始自动生成 MASTER_AUTO_POSITION=1;
4.9.3、GTID 复制和普通复制的区别
-
在主从复制环境中,主库发生过的事务,在全局都是由唯一GTID记录的,更方便做失效转移(Failover)
-
需添加三个额外功能参数:
gtid-mode=on
,enforce-gtid-consistency=true
,log-slave-updates=1
-
change master to 的时候不再需要binlog 文件名和position号,MASTER_AUTO_POSITION=1;
-
在复制过程中,从库不再依赖master.info文件,而是直接读取最后一个relaylog的 GTID号
-
mysqldump备份时,默认会将备份中包含的事务操作,以以下方式 告诉从库,然后从库直接从下一个GTID开始请求binlog
### SET @@GLOBAL.GTID_PURGED='8c49d7ec-7e78-11e8-9638-000c29ca725d:1-11';
4.9.4、GTID主从复制搭建示例
# 1、数据库配置
## 主库_master_db
[root@localhost ~]# cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql/
datadir=/data/mysql/data
socket=/tmp/mysql.sock
port=3306
server_id=1
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=master_db [\\d]>
EOF
## 从库_1_slave_db01
[root@localhost ~]# cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
port=3306
server_id=2
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=slave_db01 [\\d]>
EOF
## 从库_2_slave_db02
[root@localhost ~]# cat > /etc/my.cnf <<EOF
[mysqld]
basedir=/application/mysql
datadir=/data/mysql/data
socket=/tmp/mysql.sock
server_id=3
port=3306
secure-file-priv=/tmp
autocommit=0
log_bin=/data/binlog/mysql-bin
binlog_format=row
gtid-mode=on
enforce-gtid-consistency=true
log-slave-updates=1
[mysql]
prompt=slave_db02 [\\d]>
EOF
# 初始化数据
[root@localhost ~]# mysqld --initialize-insecure --user=mysql --basedir=/application/mysql --datadir=/data/mysql/data
# 启动数据库
[root@localhost ~]# systemctl start mysqld
# 2、构建主从
## master:192.168.137.10
## slave:192.168.137.20,192.168.137.30
## 主库
### 创建用户
mysql> create user rep@'192.168.137.%' identified by '123';
### 给用户授权
mysql> grant replication slave on *.* to rep@'192.168.137.%';
## 从库
mysql> change master to
master_host='192.168.137.10',
master_user='rep',
master_password='123',
MASTER_AUTO_POSITION=1;
mysql> start slave;