当前位置:网站首页>【mysql进阶】MTS主从同步原理及实操指南(七)
【mysql进阶】MTS主从同步原理及实操指南(七)
2022-06-27 13:36:00 【wu_55555】
0.引言
随着业务场景的深入和请求量的剧增,单库实现读写越来越趋近瓶颈,于是我们想到搭建主从库,主库负责写,从库负责读,从而实现读写分离,提高查询效率。
但是主从库之间的数据如何同步呢?很明显我们写入数据是只能通过主库操作的,因为我们要求的是主库负责写,从库负责读。我们知道binlog可以实现数据的备份和同步,那么我们是否可以通过它来实现主从同步呢?这就是我们接下来要讨论的了
1. 主从同步原理
1.1 基于binlog的主从同步原理
在正式开始讲解实操前,我们需要先了解其原理,否则只能做到知其然,而不知其所以然
什么是bin log?
bin log是我们实现主从同步的核心,同时也是我们实现数据备份、数据同步的基础,因此理解bin log是必须的。bin log本身其实就是一个二进制的日志文件,它用于记录数据表结构变化、数据变化。但是不会记录查询等操作,因为查询操作本质上不会引起数据变化。
binlog默认是关闭的,因为其本身的数据记录是会额外消耗性能的,因此我们如果不需要数据备份、同步。那么也不需要开启bin log。
binlog有三种记录模式:
(1)ROW: 行记录形式,会记录每一行的数据变化情况。优点是记得细,能够重现实现数据修改细节。缺点是会产生大量日志,消耗性能
(2)STATEMENT:状态记录形式,会记录每一条执行的SQL,然后通过SQL重放来实现数据同步。优点是节约资源,产生日志量会少很多,缺点是某些情况会导致数据不一致,比如执行的sql中
(3)MIXED:混合模式,一般情况使用STATEMENT,当STATEMENT不能满足的则自动切换为ROW进行记录
我们用一个例子来让大家理解ROW和STATEMENT的区别:
比如我现在执行的操作是 update user set level=5 where age>30。满足age>30的数据有10W条。那么statement模式记录的日志就是
update user set level=5 where age>30
,而row模式记录的日志则是
update user set level=5 where id=xxx1;
update user set level=5 where id=xxx2;
…
update user set level=5 where id=xxxN;
这里的xxxN就是age>30的行数据的id。也就是说它按照每行单独去记录变化情况
看到这里大家可能会觉得,那我就用MIXED呗,但是很多情况下,因为我们要实现主从同步,要保重主从库数据的一致性,必须要求使用ROW,才能完全体现数据变化的日志细节。
在了解了binlog之后,我们再来详细谈谈主从同步的原理细节
(1)主库将数据的变化写入到binlog中
(2)从库定期检测binlog文件是否有发生变化(检测是不需要读取文件详细内容的,可以通过文件最近修改时间和Etag来实现),如果有变化则开启一个IO线程来获取主库binlog数据
(3)同时主库会为每个IO线程启动一个dump线程,用于先IO线程发送二进制事件(也就是binlog日志数据)。发送获取的数据并不会直接给到从库进行重放,而是先放到一个中继日志(relay log)中,然后从库再从中继日志读取数据,进行重发,从而实现数据同步
(4)执行完成后,IO线程和dump线程进行睡眠状态,等待下一次数据同步再被唤醒
想象一下,数据同步过程中为什么需要中继日志,而不是让从库读取后直接重发?
中继日志的存在的目的是什么呢?这里大家发挥出自己的想象,结合以往我们所了解到的中间文件的作用,我们可以首先有一个很常见的作用:
(1)缓冲!让数据先放到中继文件中,攒够一波之后再统一处理,这样能减少IO,提高效率,这是很多中间文件的作用,但是并不是这里中继文件的作用!!!为什么?因为我们要求实时同步,等不了~ 所以攒够一波再处理的概念肯定是不通过的
(2)解耦!这才是中继文件真正的作用。我们利用发证法,假如没有中继日志,那么这个线程很明显要处理一条龙的服务,从读取binlog到重放binlog,这个操作会让这个线程很容易阻塞,因为重发sql时可能会涉及锁表、锁行的操作,不可避免的会造成一些延迟,因此我们读binlog的时候就不能重发,先将binlog实时同步过来,然后再单开一个SQL线程来重放binlog,这才是正确的处理方式。因此我们需要一个日志来帮我们先把同步的binlog记录一下,SQL线程就可以放心的做自己的重放工作,数据同步完成后中继日志会被删除。
了解到这里,相信大家对主从同步的原理已然明了,最终咱们总结下这其中的几个关键词:bin log
,relay log
,io 线程
,dump 线程
,sql 线程
,如果你能够解释出这些关键词的作用,说明你已经掌握其原理过程。
但是还有一个问题,咱们还没解决:
主从同步的延迟问题:
(1)主库在记录binlog时是顺序写,效率很高,而从库的SQL线程再重放binlog时,却并不是顺序的,而是随机的。
这里要理解为什么是随机的而不是顺序的?
这里的顺序和随机指的是磁盘IO的读写顺序和随机。因为写入binlog时,是预先申请好磁盘空间的,其空间是连续的,因此写入的binlog在磁盘物理位置上是挨在一起的,即是顺序的。而重发binlog时,相当于我们重新执行sql,其sql操作的数据存放位置基本上是不可能在磁盘中连续的,也就是说起存放位置随机分布,因此我们说是随机的。随机的读写相比较于顺序的读写所需耗费的时间肯定更多
(2)从库SQL线程在重放binlog时,当同时有查询操作出现时,可能会出现锁表、锁行等锁操作,那么就需要等待锁释放的时间,而SQL线程又是单线程的,IO线程依旧在同步binlog,SQL线程这边还在等待着,延迟自然而然就产生了
那么怎么解决延迟问题呢?
首先我们要把握问题发生且我们能够干预的关键点:
从库同步的慢,从而导致了延迟,同步慢的原因:一是同步时有查询操作或者同步本身的操作产生了锁竞争;二是服务器的本身性能问题,我们知道服务器性能越高的,各方面的执行效率肯定越高,基于以上两点,我们提出以下解决方案:
(1)从库使用更好的服务器,这样从库处理的速度更快,则延迟更小
(2)部署一主多从,减少从库的读取压力,提高查询效率,减少锁竞争,从而减少同步延迟
(3)使用缓存数据库,如redis,减少读压力
(4)多主多从,从业务上拆分成多个库,并且将各个库放到不同的服务器上,从而减少单库同步的压力
(5)部署集群,减少压力
以上的解决方案当然都是需要增加服务器成本的,那么有没有从根本上解决这个延迟问题的方案呢?这就是mysql在5.7版本后做出的优化了,也就是我们下面要讨论的MTS主从同步原理
1.2 MTS主从同步原理
MTS:Multi-Threaded Slave,并行复制,实际上早在mysql5.6版本时就有基于库的并行复制,但是5.6版本的基于库的并行复制的性能并不高,当只有一个库时,其性能甚至还不如单线程,因此5.7版本时,做出了优化,推出了真正的并行复制
MTS的核心概念在于多线程并行重放binlog,但是并不是所有的binlog都能并行重放,有些操作可能涉及锁竞争,那么就不能并行执行。
MTS中除了SQL线程,还创建了多个WORK线程,IO线程不断接收bin log,写入到relay log中,SQL线程读取relay log,并且判断哪些事件可以并行回放,哪些只能串行回放。并行回放的会分发给WORK线程并行回放。串行回放的就由SQL线程自己回放。
那么如何判定哪些事件可以并行回放?
通过组提交来实现,即对事务进行分组,我们认为一个组提交的事务都是可以并行回放的,那么怎么判定事务可以处于同一个组呢?那就是看主库中事务执行时,是否能够同时提交成功,或者说同时处于prepare阶段的所有事务,都是可以同时提交的
这里需要大家了解mysql事务两阶段提交的流程,简单来说就是一个事务提交完成是需要经历两个阶段的,事务执行,将数据更新到内容后,会先写入redo log,这时事务处于prepare状态,再写入bin log,然后提交事务,并将redo log标注为commit状态,这样整个事务才算提交完成。经历了prepare,commit两个阶段
只要能同时处于prepare状态的事务,说明事务间是没有锁竞争的,那么就是可以并行执行的,同时也是可以并行回放的。如果有锁竞争的,那么该事务肯定要等待竞争事务先执行完,释放锁后才能执行,也就不可能同时处于prepare状态
所以我们接下来要解决的问题就是:
1、如何知道哪些事务处于prepare状态?
mysql5.7引入了两个变量
(1)sequence_number
: 序列号,每个事务对应一个,顺序增长,当事务提交后便会得到自己的sequence_number
(2)last_committed
: 表示事务提交时,上次事务提交的编号,事务进入prepare阶段后,会将自己的last_committed更新为上次提交事务的sequence_number,这样有相同last_committed的事务就是同一组。
这两个变量都会记录到bin log中,并且其作用域是文件范围内,也就是说换了一个bin log文件,其值就会从0开始计算。于是乎通过last_committed我们也就知道了哪些事务是同一组的。
2、在binlog中如何标注哪些事务是同一组的?
其实我们上面已经讲到了,是通过last_committed信息来标注,但这里我们要拓展一下,mysql5.7中将sequence_number和last_committed信息属于组提交信息,组提交信息是存放到GTID事件中的,每个事务会有自己的GTID事件。
GTID默认是关闭的,如果关闭时会讲组提交信息存放到匿名GTID事件中(Anonymous_Gtid),如果开启了,就会存储到每个事务自己的GTID事件中,每个事务执行前都会添加一个GTID事件,用于记录当前的全局事务ID
MTS并发复制流程
在了解了并发复制的原理后,我们再来整体的梳理一遍其流程:
(1)主库将数据的变化写入到binlog中
(2)从库定期检测binlog文件是否有发生变化,如果有变化则开启一个IO线程来获取主库binlog数据
(3)同时主库会为每个IO线程启动一个dump线程,用于先IO线程发送二进制事件(也就是binlog日志数据)。发送获取的数据并不会直接给到从库进行重放,而是先放到一个中继日志(relay log)中
(4)从库的SQL线程从relay log中读取事务后,会获取该事务的组信息,拿到sequence_number和last_committed
(5)从库会记录已经执行了的事务的sequence_number的最小值,将其存放到low water mark变量中,简称lwm
(6)lwm与取出事务的last_committed比较,如果last_committed比lwm更小,说明取出事务与当前执行组为同组(本组事务的sequence_number的最小值肯定大于last_committed)。则SQL线程会找到一个空闲的WORK线程,如果有空闲的,就会直接重放这个事务,如果没有空闲的,SQL线程就会处于等待状态,直到有一个空闲的WORK线程为止。
(7)如果last_committed等于大于lwm,则说明取出事务与当前执行组不是同一组,则取出事务需要等待。
如下图所示,当获取到事务4时,因为已经执行的事务的最小sequence_number是3,则lwm是3
而事务4的last_committed为3,是等于lwm的,则知道事务3和事务4不是一组
当继续执行到事务5时,因为已经执行过事务4了,则lwm=4,而事务5的last_committed=3,小于lwm,则事务5与事务4同组,可以并发复制
2. 主从同步实操
了解完主从同步的原理后,我们来实际演示下配置流程
2.1 环境准备
首先这里需要准备两台mysql服务器,一台作为主库,一台作为从库,我这里选用mysql8.0。并且创建一些数据库和表,添加一些数据进去作为同步测试
2.2 实操
1、因为是基于binlog的,需要在主从库都开启binlog,并且开启主库的GTID,修改主库配置文件
sudo vim /etc/my.cnf
修改内容
log-bin=mysql-bin #二进制文件名称
binlog-format=ROW #二进制日志格式
server-id=1 #要求各个服务器的id必须不一样
gtid_mode=ON # 开启GTID
enforce_gtid_consistency=ON
log_slave_updates=ON
2、重启主库
service mysqld restart
3、查询binlog是否开启成功,登陆主库执行
show master status;
4、给连接主库进行数据同步的账号赋权,登陆主mysql执行:
grant replication slave on *.* to 'root'@'%';
# 刷新权限
flush privileges;
5、修改从库mysql配置文件
sudo vim /etc/my.cnf
修改内容
log-bin=mysql-bin # 开启bin log,配置binlog文件名
binlog-format=ROW #二进制日志格式
server-id=2 #要求各个服务器的id必须不一样
gtid_mode=ON # 开启GTID
enforce_gtid_consistency=ON
log_slave_updates=ON
slave-parallel-type=LOGICAL_CLOCK # 开启MTS相关配置,设置为LOGICAL_CLOCK实现更小粒度的并行复制
slave-parallel-workers=16 # work线程数
slave_pending_jobs_size_max = 2147483648
slave_preserve_commit_order=1 # 保证事务按照relay log记录的顺序回放,配合LOGICAL_CLOCK使用
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
6、重启从库
service mysqld restart
7、声明主库,登陆从库执行:
# 登陆
mysql -uroot -p123456
#连接主服务器
change master to master_host='192.168.244.23',master_user='root',master_password='123456',master_port=3306,master_log_file='mysql-bin.000001',master_log_pos=157;
#启动slave
start slave;
8、查看从库状态
# 表格形式输出,适合在数据库连接工具中使用
show slave status;
# 行形式输出,适合在命令行窗口中使用
show slave status\G;
这里发现报错:Authentication plugin 'caching_sha2_password' reported error
根据报错信息来看是校验模式设置为了caching_sha2_password
,我们登陆主库,查看一下
SELECT plugin FROM mysql.`user` where user = 'root';
果然是,修改其为mysql_native_password
即可,执行
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
同时因为我这里从库是根据主库服务器克隆来的,所以会导致两个mysql的uuid一致,因此也需要再更改一下,修改从库auto.cnf
配置文件:
# 进入mysql安装目录
cd /var/lib/mysql
vim auto.cnf
将uuid修改得与之前不一致即可
修改完重启mysql
其次还需要注意
,如果你之前没有开启GTID,中间产生了一些事务,而后续又开启了GTID,非GTID事务和GTID事务之间是不能同步的,这时就需要你将binlog的同步位置,调整到开启GTID之后
# 查询当前binlog位置,主库中执行
show master status;
# 从库中修改同步位置
stop slave;
change master to master_log_file='mysql-bin.000003',master_log_pos=197;
start slave;
# 查询slave状态
show slave status\G;
如图所示无报错即可
9、修改主库数据,任意修改了一条数据
10、查看从库对应的表数据
同步成功!!
总结
本期的分享就到此结束了,一定要亲自动手实操试试,只有自己尝试,才会发现更多的问题,才能掌握的更加牢靠
关注公众号 Elasticsearch之家,优先掌握更多内容
边栏推荐
- Pre training weekly issue 51: reconstruction pre training, zero sample automatic fine tuning, one click call opt
- 【问题解决】Tensorflow中run究竟运行了哪些节点?
- After 2 years of outsourcing, I finally landed! Record my ByteDance 3 rounds of interviews, hope to help you!
- scrapy
- Cool in summer
- 外包2年的我终于上岸了!记录我的字节跳动3轮面试,希望帮助到大家!
- 《预训练周刊》第51期:重构预训练、零样本自动微调、一键调用OPT
- 重读经典:《The Craft of Research(1)》
- To understand again is the person in the song
- 一次性彻底解决 Web 工程中文乱码问题
猜你喜欢
打印输出数(递归方法解决)
以前国产手机高傲定价扬言消费者爱买不买,现在猛降两千求售
Kyndryl与Oracle和Veritas达成合作
Infiltration learning diary day20
After 2 years of outsourcing, I finally landed! Record my ByteDance 3 rounds of interviews, hope to help you!
【PHP代码注入】PHP语言常见可注入函数以及PHP代码注入漏洞的利用实例
清华&商汤&上海AI&CUHK提出Siamese Image Modeling,兼具linear probing和密集预测性能!...
[acwing] explanation of the 57th weekly competition
类模板中可变参的逐步展开
【周赛复盘】LeetCode第81场双周赛
随机推荐
全球芯片市场或陷入停滞,中国芯片逆势扩张加速提升自给率
AXI总线
dynamic programming
实现WordPress上传图片自动重命名的方法
To understand again is the person in the song
Learning records of numpy Library
scrapy
How to set the compatibility mode of 360 speed browser
Pre training weekly issue 51: reconstruction pre training, zero sample automatic fine tuning, one click call opt
Summary of redis master-slave replication principle
A pang's operation record
crane:字典项与关联数据处理的新思路
赛迪顾问发布《“十四五” 关键应用领域之数据库市场研究报告》(附下载)
Domestic database disorder
Using FRP tool to realize intranet penetration
Infiltration learning diary day20
PLM还能怎么用?
Implementation of recruitment website based on SSM
Privacy computing fat offline prediction
Summary of basic usage of command line editor sed