1. 介绍
需要2台及以上节点,通过binlog在从库解析回放实现数据复制同步。
搭建步骤:
https://www.processon.com/mindmap/60813d46e401fd53c7b675cf
2. 传统复制 (Classic)
2.1 复制前提
a. 准备2个以上MySQL实例
db01 172.16.1.51(内网)
db02 172.16.1.52
b. 【主库】开启binlog日志。
mysql> select @@log_bin;
mysql> select @@log_bin_basename;
c.确保主从环境server_id和server_uuid不同
1)查看
mysql> select @@server_id;
mysql> select @@server_uuid;
2)修改从库server_id为52,主库为51
vim /etc/my.cnf
server_id=52
3)修改从库server_uuid
rm -f /data/3306/data/auto.cnf
/etc/init.d/mysqld restart
4)确保两边都reset master;让环境干净,减少干扰
特别是防止GTID影响
2.2 搭建过程
d.主库创建复制用户并授权
mysql> create user repl@'172.16.1.%' identified with mysql_native_password by '123';
mysql> grant replication slave on *.* to repl@'172.16.1.%';
#replication slave 仅用来复制权限。
e. 同步的数据库版本最好一致(8.0.26)。
f. 从库的数据同步(mysqldump/xtrabackup/clone plugin/停库CP)。
========================================================
1)主数据库锁表
mysql> flush table with read lock; ##窗口不能关.
mysql> show master status;
+---------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000001 | 677 | | | |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
2)备份主库
mysqldump -uroot -poldboy123 -A --source-data=2 --single-transaction -R -E --triggers --max_allowed_packet=64M|gzip>/server/backup/master_`date +%F`.sql.gz
3)主数据库解锁
mysql> unlock tables;
=================主库操作结束==================
4)拷贝到从库
scp /server/backup/master_`date +%F`.sql.gz 10.0.0.52:/tmp
5)从库恢复数据
gzip -d /tmp/master_`date +%F`.sql.gz
mysql -uroot -poldboy123 ##如果报错,删除/tmp/master_2021-12-19.sql第一行,--master-data被弃用提示.
6)【从库】指定连接参数以及位置点
方法1:
[root@db02 ~]# sed -n '24p' /tmp/master_`date +%F`.sql
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000001', MASTER_LOG_POS=677;
方法2:
binlog.000001 | 677 |
CHANGE MASTER TO
MASTER_HOST='172.16.1.51',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000001',
MASTER_LOG_POS=677,
MASTER_CONNECT_RETRY=10;
###5.7以前上面的CHANGE MASTER TO语句,写到从库master.info文件里,
5.7及以后写到一张表里(mysql.slave_master_info).
mysql> select * from mysql.slave_master_info\G
*************************** 1. row ***************************
Number_of_lines: 32
Master_log_name: binlog.000001
Master_log_pos: 677
Host: 172.16.1.51
User_name: repl
User_password: 123
Port: 3306
Connect_retry: 10
帮助:
help CHANGE MASTER TO
7)从库启动专用复制线程 start slave;
mysql> start slave; ##开启同步开关
mysql> show slave status\G
成功标志:两个Yes
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#MySQL 8.0.22+:命令如下
mysql> START REPLICA;
mysql> SHOW REPLICA STATUS\G
成功标志:两个Yes
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
###重启
mysql> stop replica;
mysql> start replica;
mysql> show replica status\G
(over)
2.2 传统主从复制原理
课后作业:自己搞定主从复制原理,并要能表达出来。
面试必问:必会不够,百里挑1
1.5.6两种复制方式
2.在主库开启[binlog日志],然后从库获取主库binlog日志,在从数据库里异步回放来实现的。
这个过程是[异步]的工作模式.
3.复制的时候有3个线程
2.2.1 涉及到的线程
# 主库线程
binlog dump线程,负责投递binlog。
作用:
a. 与从库进行交互
b. 监控binlog的变化
c. 投递binlog给从库
监控:
show processlist;
##Binlog Dump | 2164 | Source has sent all binlog to replica; waiting for more updates
# 从库线程
IO线程 :
a. 负责连接主库
b. 和主库的Binlog Dump交互
c. 接收和存储主库的binlog日志,存储到从库relay-log(中继日志)中
SQL线程
a.回放relay-log中的日志应用到从数据库中。
b.记录回放的位置到relay-log.info中,8.0没有此文件了,而是记录到了表里。
主从状态监控:
show slave status \G
2.2.2 涉及到的文件
# 主库:
binlog:二进制日志。
# 从库:
relay-log :中继日志,从库用来临时存储接收到的binlog日志。
master.info: 存放连接主库的信息(ip、port、user、password、已经获取的binlog位置点)
5.7以上在表中,5.7以前是磁盘上文件。
relay-log.info:存储SQL线程回放过的日志位置信息。 5.7以上在表中,5.7以前是磁盘上文件。
8.0主从复制文件转成表变化:
原始master.info文件转成了table,mysql.slave_master_info
db02 [(none)]>select * from mysql.slave_master_info\G
*************************** 1. row ***************************
Number_of_lines: 32
Master_log_name: mysql-bin.000002
Master_log_pos: 156
Host: 172.16.1.51
User_name: repl
User_password: 123
Port: 3306
Connect_retry: 60
relay-log.info文件转成了table mysql.slave_relay_log_info
db02 [(none)]>select * from mysql.slave_relay_log_info\G
*************************** 1. row ***************************
Number_of_lines: 14
Relay_log_name: ./db02-relay-bin.000002
Relay_log_pos: 1270
Master_log_name: mysql-bin.000002
Master_log_pos: 1055
原始master.info文件转成了table,mysql.slave_master_info
db02 [(none)]>select * from mysql.slave_master_info\G ###记录io线程存放binlog位置点
mysql> select * from mysql.slave_master_info\G
*************************** 1. row ***************************
Number_of_lines: 32
Master_log_name: binlog.000001 #读取到文件名
Master_log_pos: 1216 ##位置点
Host: 172.16.1.51
User_name: repl
User_password: 123
Port: 3306
Connect_retry: 10
relay-log.info文件转成了table mysql.slave_relay_log_info
mysql> select * from mysql.slave_relay_log_info\G
*************************** 1. row ***************************
Number_of_lines: 14
Relay_log_name: ./db02-relay-bin.000003 ##回放的文件
Relay_log_pos: 240 #回放到数据库的中继日志位置
Master_log_name: binlog.000001
Master_log_pos: 1216
Sql_delay: 0
2.2.3 画图说明主从复制原理 ******
a. 从库执行change master to 命令,将主库连接信息和binlog位置信息写入master.info文件或者slave_master_info表中
b. 从库执行start slave,启动从库的IO,和SQL线程
c. IO线程读取主库链接信息,连接主库,主库派生dump线程(dump自动监控binlog变化)
d. IO线程根据binlog位置点信息,获取最新的binlog
e. dump 截取并投递binlog给从库IO线程 ,主库不关心投递的结果。
f. IO线程收到binlog(缓存),立即更新master.info 或者slave_master_info表
g. 缓存binlog数据写入relaylog
h. SQL线程读取relaylog.info或者slave_relay_log_info表,获取到上次回放到的位置点,继续往后回放,回放完成后,再次更新relaylog.info信息
i. 回放过的relaylog,relay_log_purge线程会定期删除这些日志。
2.3 复制监控(ZABBIX写脚本插件)
2.3.1 监控方法:
a. 自带命令show slave status\G
b. pt-table-checksum(主从数据校验)/pt-table-sync(同步)/pt-heartbeat(监控主从心跳)
pt-heartbeat监控主从
https://baijiahao.baidu.com/s?id=1654616467161901898&wfr=spider&for=pc
c. orch estrator 主从拓扑可视化工具
2.3.2 自带命令 show slave status \G
主从复制状态核心参数:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
主从延迟判断方法:
方法1:
Seconds_Behind_Master: 0 #从库更新延迟秒数
方法2:
#更科学方法;主库写时间戳到一张表里,从库读出来和当前从库时间比对,进而确定是否延迟
pt-heartbeat监控主从
https://baijiahao.baidu.com/s?id=1654616467161901898&wfr=spider&for=pc
方法3:看状态中的IO的binlog位置,SQL对应binlog位置,确定延迟的日志量。
# 主库的复制信息
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for source to send event
Master_Host: 172.16.1.51
Master_User: repl
Master_Port: 3306
Connect_Retry: 10
Master_Log_File: binlog.000001
Read_Master_Log_Pos: 1230
Relay_Log_File: db02-relay-bin.000002
Relay_Log_Pos: 874
Relay_Master_Log_File: binlog.000001
# 从库SQL线程已经回放的中继日志信息
mysql> select * from mysql.slave_relay_log_info\G
Relay_log_name: ./db02-relay-bin.000002
Relay_log_pos: 874
Master_log_name: binlog.000001
Master_log_pos: 1230
说明:通过后两条对比master.info,判断主从延时的日志量。
mysql> show replica status\G
# 主从复制线程故障
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Last_IO_Errno: 0 ##IO故障号码
Last_IO_Error: ##IO故障提示
Last_SQL_Errno: 0 ##SQL故障号码
Last_SQL_Error: ##SQL故障提示
# 过滤复制相关信息
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:
2.4 复制的故障分析及处理
讲解见导图:
https://www.processon.com/mindmap/60813d621e08534b2ef252c1
# IO线程方面:
a. 监控
Slave_IO_Running: Yes
正常状态:Yes
有问题的状态:Connecting、NO
具体问题分析:
Last_IO_Errno: 0
Last_IO_Error:
IO都干什么活?
(1) 连接主库 (Connecting )
通信故障:1.防火墙。2.网络。3.IP\PORT\USER\PASSWORD。4.连接数上限
change参数错误处理方法:
stop slave;
reset slave all;
select * from mysql.slave_master_info;
change master to
start slave;
(2) 请求日志,接收日志,落地日志(NO)
reset master;
修复方式:
stop slave;
reset slave;
CHANGE MASTER TO
MASTER_HOST='172.16.1.51',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000001',
MASTER_LOG_POS=1230,
MASTER_CONNECT_RETRY=10;
(3) server_id server_uuid重复
修改为不同即可。
# SQL线程方面:
Slave_SQL_Running: Yes
SQL主要工作是什么?
回放ralay log,执行里面的SQL语句。
其实研究SQL线程故障,就是在研究SQL语句为什么执行不了?
a. 创建的对象(库、表、用户、索引。。。。),从库已经存在了。
b. insert \ update \ delete \alter \drop 从库对象不存在。
c. 约束冲突
d. 配置不兼容(比如不同版本)
原因:
1. 误操作 chang master to 指定位置点不对
2. 从库被写入了
3. 业务繁忙时,从库宕机了
4. 主从切换时,没有正确操作(锁定原主库+binlog写入)
5. 双主结构,没有正确使用
处理原则:
1. 一切以主库为准
2. 双主屏蔽多写。
3. 从库尽量设置只读
如果出现了怎么处理:
1. 冲突操作回退回去
2. 从库跳过错误(配合pt-checksum/pt-sync) ###8.0.26
mysql> stop slave;
mysql> set global sql_slave_skip_counter=1; ###8.0.26好用
mysql> start slave;