Mysql主从复制案例
Mysql主从复制案例
各种复制架构
主从复制原理
主从复制相关线程
- 主节点:
dump Thread: 为每个Slave的I/O Thread启动一个dump线程,用于向其发送binary log events
- 从节点:
I/O Thread: 向Master请求二进制日志事件,并保存于中继日志中
SQL Thread: 从中继日志中读取日志事件,在本地完成重放
跟复制功能相关的文件:
- master.info :用于保存slave连接至master时的相关信息,例如账号、密码、服务器地址等
- relay-log.info :保存在当前slave节点上已经复制的当前二进制日志和本地relay log日志的对应关系
- mysql-relay-bin.00000#: 中继日志,保存从主节点复制过来的二进制日志,本质就是二进制日志
二进制日志功能
【安装好mysql再来查询
】
这里讲一下二进制日志是干啥的,你只要开启了二进制日志的功能
sql_log_bin=ON
:开启二进制日志记录的功能,你执行一个mysql语句他就会记录一下,他在mysql默认是开启的就不用管了
log-bin=路径/文件前缀
:开启二进制日志功能,这个可以直接写在/etc/my.cnf里,一主一从的步骤里有介绍
一个是记录,一个是开启,如果你只有记录,没有开启是没有二进制日志的,所以二者缺一不可
先看看怎么查
1 | [root@master ~]# mysql -uroot -p123456 -e "select @@log_bin;" |
如果你看到两个都是1或者ON那就是全部开启了
一主一从
master:192.168.48.128
slave:192.168.48.11
安装mysql
自行关闭firewalld,selinux
操作节点:【所有节点】
二进制安装(适合新机)
1 | #你可以选择用我的脚本进行安装二进制的mysql |
其他安装
其他方式自行安装,我就不解释了,只要确保同个版本差不多就行了8.0.x这些。
主节点配置
操作节点:【master】
修改/etc/my.cnf
1 | #其他安装mysql的方法,你只需要确保有如下选项就行了 |
sever-id
:必须是整个集群里面唯一的,不能重复
log-bin
:后续会在/data/logbin生成qylog.000001的二进制文件,你可以自定义路径和文件名不用加后缀
如果是用了我的脚本安装的mysql可以直接执行下面的语句,不是的话就看上面那个步骤手动修改
1 | cat > /etc/my.cnf << "EOF" |
由于前面不是开启了二进制文件的功能吗
我的路径是:/data/logbin/qylog
,其中/data/logbin
并不存在,你要手动创建,不然开启mysql会报错
1 | mkdir -p /data/logbin |
此时二进制文件已经创建成功
1 | [root@master ~]# ll /data/logbin/ |
创建用于主从复制的账号
1 | [root@master ~]# mysql -uroot -p123456 -e "show master status" |
1 | #创建repluser用户,密码是123456,允许从192.168.48.0来连接master的mysql |
从节点配置
操作节点:【slave1】
修改/etc/my.cnf
1 | #其他安装mysql的方法,你只需要确保有如下选项就行了 |
sever-id
:必须是整个集群里面唯一的,不能重复
log-bin
:后续会在/data/logbin生成qylog.000001的二进制文件,你可以自定义路径和文件名不用加后缀
如果是用了我的脚本安装的mysql可以直接执行下面的语句,不是的话就看上面那个步骤手动修改
1 | cat > /etc/my.cnf << "EOF" |
由于前面不是开启了二进制文件的功能,还有开启中继日志的功能吗
我的路径是:
log-bin=/data/logbin/qylog
,其中/data/logbin
并不存在
relay_log=/data/relaylog/relay-log
,其中/data/relaylog
并不存在
你要手动创建,不然开启mysql会报错
1 | mkdir -p /data/logbin /data/relaylog |
启动复制线程
操作节点:[slave]
使用有复制权限的用户账号连接至主服务器
1 | mysql -u root -p123456 |
CHANGE MASTER TO
MASTER_HOST=‘192.168.48.128’, master的ip
MASTER_USER=‘repluser’, master创建的主从复制的账号
MASTER_PASSWORD=‘123456’, master创建的主从复制的账号的密码
MASTER_LOG_FILE=‘qylog.000001’, master查看的二进制文件
MASTER_LOG_POS=157, master查看的二进制文件的起始位置
MASTER_DELAY=10; 延迟更新10s
启动复制线程
1 | start slave; |
确保有两个yes就行了
如果你想重置线程可以用以下命令
1 | stop slave; |
有一个重要的点,因为二进制日志是记录你的操作的嘛,我们在开启二进制日志之后,不是在主节点
创建了一个repluser用户吗,那这个操作肯定也被记录,然后这不是主从复制了吗,这里肯定,也会同步,也会运行,二进制日志本身就是一个sql文件,普通cat是看不了,你得用这个命令
1 | [root@master ~]# mysqlbinlog -uroot -p123456 /data/logbin/qylog.000001 -v |
所有既然主从复制了,那在从节点,应该也运行了这个二进制文件,就说明从节点也有这个账号
1 | select host,user from mysql.user; |
测试
接下来在主节点导入测试数据
全选复制粘贴退出mysql
运行
1 | cat > hellodb.sql <<"EOF" |
在主节点导入
1 | #导入 |
接下来看看从节点是否有成功实现主从复制
1 | [root@slave ~]# mysql -uroot -p123456 |
至此,一主一从的主从复制实验成功,你必须理解,主要是通过二进制日志来依靠传输的
案例
实战案例:将已有的MySQL8.0
单机架构变成主从复制架构
这台单机,可能运行很久了,有很多数据了,现在要让他变成主从架构
前提:两者开启二进制日志功能
1.mysqldump备份所有数据库
2.创建传输用户并且授权
3.将备份scp复制到从节点
4.从节点开启read-only等参数
5.从节点设置主从复制连接
6.从节点进行还原备份
7.开启主从复制,start slave;
1 | #已有的master节点,备份所有的数据库 |
一主多从
master:192.168.48.128
slave1:192.168.48.11
slave2:192.168.48.10
接下来在前面的一主一从复制架构,变成一主多从,一样,如果这个mysql的主节点是已经运行很久了,你就要全部备份,拉到从节点,注意新的从节点要安装和另外一个从节点一样的mysql版本(这里我就略过安装教程了)
,然后只需要在新从节点设置好/etc/my.cnf和主从连接信息,关闭二进制记录,还原all.sql文件,开启二进制记录,开启主从复制即可
这里就接着那个案例
来
1 | #在案例的时间点之后,又运行了一段时间,此时你必须重新备份全部数据库,拉到从节点 |
至此一主多从成功!
级联复制
master:192.168.48.128
slave1:192.168.48.11(充当级联slave中间节点)
slave2:192.168.48.10
这个实验,我就只用了一个slave2,slave3和slave2的操作一样的
为啥会有级联呢!
就好比前面的一主多从,他要进行多个复制任务给从节点,那主节点的压力是不是大一点,那么就有了级联,把复制任务分担给从节点
这里还有一个问题
首先我们都知道主从复制的原理就是,主节点通过二进制日志bin
传输数据到从节点的io线程接收写到中继日志relay
,那后面的slave2
和slave3
应该也是这样,只需要接收slave1
的bin的数据就行了是不是
错!大错特错!!!!!!
slave1的bin的数据从哪里来,他自己本身就是生成自己自身的数据,他怎么可能有主节点的数据呢?是不是得从自身的relay来,所以后面会讲到一个参数,就是可以让relay的数据传输过来的时候自动写入到bin,那就后面再说
这里延续前面的一主多从
1 | #首先我得把slave2的一些信息删除,脱离原来一主多从 |
在中间节点slave1
启用以下配置,实现中间slave节点能将master的二进制日志在本机进行数据库更新,并且也同时更新本机的二进制,从而实现级联复制
1 | [mysqld] |
重启mysql
1 | systemctl restart mysqld |
接着在新的从节点(slave2)进行操作,假设他是新的,你要进行的是,配置my.cnf,创建主从复制账号和授权,还有因为他是新的,没有数据,你还要在从节点
备份全部数据库到从节点
1 | #新的从节点 |
主主复制
master:192.168.48.128
slave1:192.168.48.11(让他变成master主节点)
slave2:192.168.48.10
容易产生的问题:数据不一致;因此慎用
配置步骤
(1) 各节点使用一个惟一server_id
(2) 都启动binary log和relay log
(3) 创建拥有复制权限的用户账号
(4) 均把对方指定为主节点,并启动复制线程
keepalived提供vip地址,实现数据库集群高可用,这个点后面再说
双主的意思就是,这两个节点互为主从,实现双向复制,我是主的时候你是从,你是主的时候我是从
1 | #将slave1的/etc/my.cnf里的read only相关参数要删掉,既然都是主节点,那么肯定要可读可写,保留如下 |
1 | #现在接着配置主节点的主从复制连接指向第二个主节点,slave1 |
测试两个节点各自插入新数据,看看对方有没有实现双向复制
1 | #第一个主节点 |
1 | 如果你两个节点都同时插入数据,会出现一个主键的冲突 |
所以要这样配置
添加auto_increment_offset和auto_increment_increment
1 | #slave1 |
两个节点重启服务
1 | systemctl restart mysqld |
再次两个节点同时插入数据
1 | #第一个节点 |
看前面的架构图,两主一从,其实现在的状况是,数据从master写入到slave再到slave2的,slave2是没有连接master,所以只能后期写脚本,监测keepalived的vip变化,一旦变化,就去修改那个主从复制连接中的master ip去变化。
半同步复制
官方文档
1 | https://dev.mysql.com/doc/refman/8.0/en/replication-semisync.html |
经过前两个图的理解,现在就开始实操
1 | #接着上面一个主主的案例下来,如果不是,你只需要保留/etc/my.cnf有如下选项即可 |
1 | #三个节点的复制关系都要删掉 |
master导出全部备份给slave1和slave2,创建复制账号,关闭二进制日志记录
还原数据库,开启二进制日志记录,自行验证是否同步,这里就略过了
(注意是一主二从)相当于重新开始了
半同步复制的一些参数
1 | #master节点 |
rpl_semi_sync_master_enabled
:是否启用半同步复制功能。ON
表示启用,OFF
表示禁用。rpl_semi_sync_master_timeout
:主库等待从库响应的超时时间(单位:毫秒)。超时后主库会切换为异步复制。rpl_semi_sync_master_trace_level
:日志记录的详细程度,数值越大记录越详细。rpl_semi_sync_master_wait_for_slave_count
:主库提交事务时需要等待的从库数量。rpl_semi_sync_master_wait_no_slave
:当没有从库连接时,主库是否继续使用半同步复制。ON
表示继续等待,OFF
表示切换为异步复制。rpl_semi_sync_master_wait_point
:半同步复制的等待点。AFTER_SYNC
表示在同步完成后等待,AFTER_FLUSH
表示在数据刷新到磁盘后等待。
1 | #master |
Rpl_semi_sync_master_clients
:当前连接到主库并支持半同步复制的从库数量。
Rpl_semi_sync_master_net_avg_wait_time
:半同步复制中网络平均等待时间(单位:微秒)。
Rpl_semi_sync_master_net_wait_time
:半同步复制中网络总等待时间(单位:微秒)。
Rpl_semi_sync_master_net_waits
:半同步复制中网络等待的总次数。
Rpl_semi_sync_master_no_times
:半同步复制未成功(切换为异步复制)的次数。
Rpl_semi_sync_master_no_tx
:半同步复制未成功时的事务数量。
Rpl_semi_sync_master_status
:当前半同步复制的状态,ON
表示启用,OFF
表示禁用。
Rpl_semi_sync_master_timefunc_failures
:半同步复制中时间函数失败的次数。
Rpl_semi_sync_master_tx_avg_wait_time
:事务平均等待时间(单位:微秒)。
Rpl_semi_sync_master_tx_wait_time
:事务总等待时间(单位:微秒)。
Rpl_semi_sync_master_tx_waits
:事务等待的总次数。
接下来进行测试
1.测试都启动的情况下,一主二从,是否正常的主从同步
1 | #主节点 |
2.接下来测试,slave关闭mysql模拟宕机的操作,再次进行插入数据
1 | #slave2 |
3.接下来,让最后一个slave1也宕机看看是什么情况
1 | #slave1 |
- 第一次插入操作等待 10 秒,是因为主节点在半同步复制模式下等待从节点确认,但超时了。
- 第二次插入操作没有等待,是因为主节点已经检测到没有从节点连接,直接以异步复制模式执行。
4.恢复所有的slave节点,看看有无同步
1 | #slave1和slave2 |
至此半同步复制成功
复制过滤器
让从节点仅复制指定的数据库,或指定数据库的指定表
复制过滤器两种实现方式:
(1) 服务器选项:主服务器仅向二进制日志中记录与特定数据库相关的事件
缺点:基于二进制还原将无法实现;不建议使用
优点: 只需要在主节点配置一次即可
只需要在主服务器配置,如果我不想同步论坛,只想同步其他的,只需要禁用论坛的二进制日志,就行了,但是这就有一个问题,你论坛的二进制日志都禁用了,那你这个论坛的这个数据库突然出现问题了,数据怎么办?这就是缺点
这个禁用二进制,不用禁用log_bin选项,看下面的来配置黑白名单就行
注意:此项和 binlog_format相关
参看:https://mariadb.com/kb/en/library/mysqld-options/#-binlog-ignore-db
1 | #只同步db1和db2,也就是只有这两个会开启二进制日志,其他数据库不会 |
注意:
1 | This option will not work with cross-database updates with statement-based logging. See the Statement-Based Logging section for more information. |
来测试吧
1 | #master |
(2) 从服务器SQL_THREAD在relay log中的事件时,仅读取与特定数据库(特定表)相关的事件并应用于本地
缺点:会造成网络及磁盘IO浪费,在所有从节点都要配置
优点: 不影响二进制备份还原
从服务器上的复制过滤器相关变量
白名单和黑名单二选一
,复制库和复制表,以及通配符类型三选一(白名单和黑名单二选一)
,不然会出现数据不一致的情况
1 | replicate_do_db=db1 #指定复制库的白名单,选项不支持多值,只能分别写多行实现 |
1 | #删除master的第一步的配置,保留如下 |
在所有的从节点,注意是所有的从节点
1 | [mysqld] |
master创建测试数据
1 | mysql -u root -p123456 |
slave查看从节点
1 | USE db1;SELECT * FROM test1; |
GTID复制
全局复制Id
背景
💥 传统复制机制的问题(基于 binlog 位置点)
- 多个客户端(如图中)分别发起事务 1001、1002、1003,主库依次执行,写入 binlog。
- 主库通过
只有一个
的 dump 线程把 binlog 发送给从库。 - 从库通过 IO 线程读取 binlog,通过 SQL 线程 一个一个串行执行事务。
❗问题来了:
- 从库自身也有客户端访问,可能也会产生事务,编号也可能是 1001、1002、1003(编号只是应用内部的业务编号,不是数据库唯一标识)。
- 主库传过来的 binlog,只记录了操作内容、表名、数据改动,但没有全局唯一事务 ID。
- 所以从库根本分不清:这个 1001 是主库的?还是我自己产生的?
- 为了避免数据冲突、保证顺序一致,从库只能串行地照着 binlog 执行事务,而不能乱序执行或并发执行。
正是因为分不清,怕双方的事务乱序执行,所以才会选择串行执行,确保安全
而且如果一个事务特别特别慢,他会拖累整个串行执行的进度,效率慢
GTID(全局事务标识符) 从 MySQL 5.6 开始引入,建议使用 5.7 及以上版本,更稳定可靠。
在 MySQL 5.7 中,即使未开启 GTID,也会生成匿名 GTID,具备一定追踪能力。
GTID 复制的优势包括:
- 支持
master_auto_position=1
,无需指定 binlog 文件和 POS 点,主从复制更简化。
就是你那个主从复制连接里,不用记录了是哪个二进制文件和起点了,有了这个参数就可以自动识别,前提是
主从的数据库要一致
- 实现了事务级的幂等性,重复执行事务不会出错。
- 配合 5.6 的库级并行复制和 5.7 的事务级并行复制(逻辑时钟机制),显著提升复制并发性能,降低延迟。
- 可安全支持多个 dump 线程 + 多 SQL 线程 并发复制。
GTID 优点:
- 保证事务全局统一
- 截取日志更加方便。跨多文件,判断起点终点更加方便
- 判断主从工作状态更加方便
- 传输日志,可以并发传输。SQL回放可以更
高并发
- 主从复制构建更加方便
GTID =
server_uuid
:transaction_id
,在一组复制中,全局唯一server_uuid 来源于 /var/lib/mysql/auto.cnf
GTID服务器相关选项
1 | gtid_mode=ON #gtid模式 |
测试案例:前提是主从的数据库要一致
,并且创建好复制传输的账号
#如果主服务器和从服务器数据不一致,需要先将主库数据备份还原至从库,再执行下面操作
1 | mysqldump -uroot -p123456 -A -F -E -R --triggers --single-transaction \ |
1.主节点
1 | vim /etc/my.cnf |
2.从节点(slave1和slave2都一样)
1 | vim /etc/my.cnf |
3.从节点重置前面实验的slave复制连接
1 | #slave1和slave2都执行 |
4.查看是否成功
1 | 主节点执行一个操作,我这里就执行创建一个db4数据库 |
至此gtid成功
主从复制bug集锦
1.主从复制在从节点你可能会看见
show slave status\G;
1 | Last_IO_Error: error connecting to master 'repluser@192.168.48.11:3306' - retry-time: 60 retries: 1 message: Authentication plugin 'caching_sha2_password' reported error: Authentication requires secure connection. |
原因:MySQL 8 默认使用 caching_sha2_password
作为认证插件,这种认证方式需要安全连接(SSL/TLS)。需要生成证书,如果不想生成证书,你自己现在从节点进行一次登入
如果你是级联最后的slave,主节点的地址要指向中间节点
在这种情况下,服务器将RSA公钥发送给客户端,后者使用它来加密密码并将结果返回给服务器。插件使用服务器端的RSA私钥解密密码,并根据密码是否正确来接受或拒绝连接。
1 | mysql -urepluser -p123456 -h192.168.48.128 --get-server-public-key |
然后重启slave复制线程
1 | stop slave;start slave;show slave status\G; |
以上只是临时的解决方案,还有一个办法就是
加这个参数get_master_public_key=1
如果这个不行就换get_source_public_key=1
1 | CHANGE MASTER TO |
2.在主主复制的时候,我知道不是设置了延迟复制为10秒吗,就是那个主从复制的连接最后一项
但是情况是:
第一个主节点插入数据成功复制到从节点
但是在第二个主节点,插入数据之后,就出现l下面的情况
SQL_Remaining_Delay: 3995
Slave_SQL_Running_State: Waiting until SOURCE_DELAY seconds after source executed event Master_Retry_Count: 86400
问了ai
1 | 你设置了延迟复制 MASTER_DELAY = 10; |
结果你知道吗,真的服了,两个主机的时间不一致
1 | [root@slave ~]# date |
复制的监控和维护
清理日志
1 | PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr } |
复制监控
1 | SHOW MASTER STATUS |
从服务器是否落后于主服务
1 | Seconds_Behind_Master:0 |
如何确定主从节点数据是否一致
1 | percona-toolkit |
数据不一致如何修复
删除从数据库,重新复制
复制的问题和解决方案
数据损坏或丢失
- Master:MHA + semisync replication
- Slave: 重新复制
不惟一的 server id
重新复制
复制延迟
- 升级到MySQL5.7以上版本(5.7之前的版本,没有开GTID之前,主库可以并发事务,但是dump传输时是串行)利用GTID(MySQL5.6需要手动开启,MySQL5.7以上默认开启)支持并发传输binlog及并行多个SQL线程
- 减少大事务,将大事务拆分成小事务
- 减少锁
- sync_binlog=1 加快binlog更新时间,从而加快日志复制
- 需要额外的监控工具的辅助
- 一从多主:Mariadb10 版后支持
- 多线程复制:对多个数据库复制
MySQL 主从数据不一致
造成主从不一致的原因
- 主库binlog格式为Statement,同步到从库执行后可能造成主从不一致。
- 主库执行更改前有执行set sql_log_bin=0,会使主库不记录binlog,从库也无法变更这部分数据。
- 从节点未设置只读,误操作写入数据
- 主库或从库意外宕机,宕机可能会造成binlog或者relaylog文件出现损坏,导致主从不一致
- 主从实例版本不一致,特别是高版本是主,低版本为从的情况下,主数据库上面支持的功能,从数据库上面可能不支持该功能
- 主从sql_mode 不一致
- MySQL自身bug导致
主从不一致修复方法
- 将从库重新实现
虽然这也是一种解决方法,但是这个方案恢复时间比较慢,而且有时候从库也是承担一部分的查询操作的,不能贸然重建。
- 使用percona-toolkit工具辅助
PT工具包中包含pt-table-checksum和pt-table-sync两个工具,主要用于检测主从是否一致以及修复数据不一致情况。这种方案优点是修复速度快,不需要停止主从辅助,缺点是需要知识积累,需要时间去学习,去测试,特别是在生产环境,还是要小心使用
关于使用方法,可以参考下面链接:https://www.cnblogs.com/feiren/p/7777218.html
- 手动重建不一致的表
在从库发现某几张表与主库数据不一致,而这几张表数据量也比较大,手工比对数据不现实,并且重做整个库也比较慢,这个时候可以只重做这几张表来修复主从不一致这种方案缺点是在执行导入期间需要暂时停止从库复制,不过也是可以接受的
范例:A,B,C这三张表主从数据不一致
1 | 1、从库停止Slave复制 |
如何避免主从不一致
- 主库binlog采用ROW格式
- 主从实例数据库版本保持一致
- 主库做好账号权限把控,不可以执行set sql_log_bin=0
- 从库开启只读,不允许人为写入
- 定期进行主从一致性检验
难免会有出错的地方
如果细心的你发现了小失误,可以在下方评论区告诉我,或者私信我!
非常感谢大家的热烈支持!