目录
前言
主从延迟问题又称为
“过期读”问题,是数据一致性问题;另外,对于MySQL数据库来说,任何时刻尽量
不允许大事务的执行;若要执行,则将大事务拆成一个个小的子事务来执行,这是最基本心法口诀。
1、主从架构类型
在应用系统的并发数非常大的情况下,如果没有缓存,会造成两个问题:一方面是会给数据库带来很大的压力;另一方面,从应用的层面来说,操作数据的速度也会受到影响。

我们可以用第三方的分布式缓存服务来解决这个问题,例如 Redis。使用独立的缓存服务,属于架构层面的优化。但是为了提高数据库服务的稳定性和可用性,为了减少单台数据库服务器的读写压力,在架构层面我们通常都会采用集群和主从。
集群的话必然会面临一个问题,就是不同的节点之间数据一致性的问题。如果同时读写多台数据库节点,怎么让所有的节点数据保持一致? 这个时候我们需要用到复制技术(replication),被复制的节点称为master,复制的节点称为slave。slave本身也可以作为其他节点的数据来源,这个叫做级联复制。

2、主从架构复制步骤及原理
2.1、主从的优势
主从架构基本上是现在互联网分布式架构的必备基础方案,做了主从复制的方案之后,我们只把数据写入master节点,从节点自动做数据的同步,有句话说:单点有时候是对系统一种不负责任的表现。
主从的作用:
-
高可用,解决单点问题,提高可用性:backup;
-
读写分离, 减轻负载,提高读写性能:业务读写压力大的时候,让主库专注于写,从库专注于读;

2.2、一个日志的同步过程
主从复制是怎么实现的呢?更新语句会记录binlog,它是一种逻辑日志。有了这个binlog,从服务器会获取主服务器的binlog文件,然后解析里面的SQL语句,在从服务器上面执行一遍,保持主从的数据一致。具体步骤如下:
-
1、在备库B上通过change master命令,设置主库A的ip,端口,用户名,密码,以及要从那个 位置position开始请求 binlog,这个位置包含文件名字日志偏移量;
-
2、在备库B上设置start slave命令,这时候备库会启动两个线程, slave_ io_th read 和 salve_ sq l_thread;
-
3、主库A检验完用户密码后,开始按照备库b传过来的位置,从本地读取 binlog,发送给b;
-
4、备库B拿到binlog后,写到本地文件,称为 中转日志( relay log );
-
5、 salve_ sq l_thread读取中转日志,解析处日志的命令,并到从库执行;从mysql5.6后, salve_ sq l_thread演化成了多线程( 并行复制)。

过程中涉及到两个线程和两个日志。
-
两个个线程:
-
slave_io_running的IO线程:连接master获取binlog,并且解析binlog写入中继日志;
-
slave_sql_running线程:用来解析中继日志relay log,把数据写入到从数据库;
-
-
两个日志:
-
binlog日志;
-
relay log 中继日志;
-
3、数据一致性的主从复制延迟问题
主从延迟的表现是,备库消费delay log的速度,比主库生产的binlog的速度慢。
读写分离可以一定程度低减轻数据库服务器的访问压力,但是需要特别注意主从数据一致性的问题。如果我们在master写入了,马上到slave查询,而这个时候slave的 数据还没有同步过来,怎么办?这里就涉及到主从延迟问题。
根据CAP理论,P一定存在,C和A一定要有取舍,满足了A,那么就会有C的问题,所以要平衡取舍。
那么基于主从复制的原理,我们需要明白,除了网络带宽和磁盘io外,
数据库层面主从复制到底慢在那里呢?
3.1、单线程
在早期的MySQL中,slave的SQL线程是单线程。master可以支持SQL语句的并行执行,配置了多少的最大连接数就是最多同时多少个SQL线程并行执行。而slave的SQL却只能
单线程排队执行,在主库并发量很大的情况下,同步数据肯定会出现延迟。
思考:
为什么从库上的SQL-Thread 不能并行执行呢?
举个例子,主库执行了三条SQL语句:
-
首先用户注册了一个商家信息;
-
紧接着然后更新了商家的资质信息;
-
最后校验信息不满足条件解除了合作关系;
这三条sql是不能改变执行顺序的,所以导致了从库执行sql延迟。
那么怎么减少主从延迟呢?
3.2、全同步与异步
在MySQL主从复制的过程中,
默认是采用异步复制的。也就是说,对于主节点来说,写入binlog,事务结束,就返回给客户端了,对于slave来说,接收到binlog,就完事儿了,master对slave的数据是否写入成功不关系,虽然效率高,如果主库发送前掉电,则会导致数据的丢失,存在主从数据的一致性问题。
为了减少延迟,也为了提高数据安全性,master会等待全部slave节点将数据完全同步完毕,主库收到ack才返回客户端,这样的方式叫做
全同步复制。全同步复制数据一致性高,但性能低;
这种方式虽然可以保证在读之前,数据已经同步成功了,但是带来的效率问题就是事务执行的时间会变长,它会导致master节点性能下降。
有没有更好的办法呢?既减少slave写入的延迟,又不会明显增加master返回给客 户端的时间?
3.3、SEMI-Sync半同步复制方案
针对异步和全同步的优缺点问题,
一般都会采取折中方案,取长补短,类似于hashmap的设计思想,结合了链表和数组的双重优点来满足时间复杂度。这种方案介于异步复制和全同步复制之间,叫做
SEMI
(
Semisynchronous Replication)
半同步复制的方式
。
实现过程是:主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到binlog并写到relay log中才返回给客户端。master不会等待很长的时间,但是返回给客户端的时候,保证数据至少已经写入一个slave节点了。这样即保证了数据一致性也提高了master的节点处理能力。
3.4、多库并行复制
前面的思路是采用同步异步的方式来解决延迟问题,
从库上一直只有一个线程在工作,有没有一种方案使从库可以并行执行sql,而不是排队呢?
思考:
怎么实现并行复制呢?
试想一下,如果多3条语句是在3个数据库执行,操作各自的数据库,是不是肯定不会产生并发的问题呢? 执行的顺序也没有要求。所以如果是操作三个数据库,那这三个数据库的从库的SQL线程可以并发执行,互不影响。这也是MySQL5.6版本里面支持的
多库并行复制。

3.5、异步GTID复制
但是在大部分的情况下,我们都是单库多表的情况,
在一个数据库里面怎么实现并行复制呢?
其实数据库本身就是支持多个事务同时操作的,因为他们本身就是互相不干扰的,比如这些事务是操作不同的表,或者操作不同的行,不存在资源的竞争和数据的干扰,那么在主库上并行执行的事务,在从库上肯定也是可以并行执行。比如在master上有三个事务同时分别操作三张表,这三个事务在slave上面也是可以并行执行的。所以,我们可以把那些在主库上并行执行的事务
,
通过一个编号将这些事务分组,使事务独立,
这一个组的事务在从库上面也可以并行执行,因为组与组之间的执行是互不干扰的,这个编号,
我们把它叫做 GTID(Global Transaction Identifiers),这种主从复制的方式,我们把它叫做
基于GTID的复制。

使用GTID复制的参数设置,默认是关闭的:
set global variables grid_mode=on;
无论是优化master和slave的连接方式,还是让从库可以并行执行SQL,都是从数据库的层面去解决主从复制延迟的问题。
除了数据库本身的层面之外,在应用层面,我们也有一些减少主从同步延迟的方法。
3.6、应用层面的优化
(1). 强制走主库方案
:业务对时间敏感的可以直接读主库;但是会增加主库负载,从完全库变为数据备份;
(2).
分库分表: 我们在做了主从复制之后,如果单个 master 节点或者单张表存储的数据过大的时 候,比如一张表有上亿的数据,单表的查询性能还是会下降,所以我们还可以实行分库分表。
(3).
避免大事务:
-
a. 避免高峰期修改表结构;
-
b. 一次删除大量的数据,分批删除;
-
c. 避免跨库事务;
-
d. 避免在事务中做外部调用:rpc、MQ,查询,循环调用;
(4).
sleep等一等方案
(不太优雅)
:从库查询的时候先sleep几毫秒,等待主从库binlog的同步完成,或者从库查询前先判断seconds_behind_master主从落后的时间的长度,当为0的时候再进行查询;
3.7、其他导致主从延迟的原因
(1). 全库备份的时候:如果是全库备份,整个主库都不能用,造成短暂的延迟;
(2).
大事务导致锁资源的占用
,后续sql得不到执行,不能生成binlog,导致延迟;比如日常的操作:
-
a. 修改表结构,由于mdl锁和ddl冲突,导致后续sql无法执行,主从延迟;
-
b. 修改索引:修改索引会导致索引的重建,尤其主键索引,耗时较长;
-
c. 删除大量的数据,数据有风险,容易引起主从延迟、磁盘io过高等问题,需要和dba同学沟通后才能执行;
(3).
备库压力大:数据库的“双一”配置在高峰期可能出现主从的延迟,数据压力大,从库的读压力导致写操作延迟,常用配置参数如下:
-
sync_binlog=1:每提交一次事务binlog就立即刷到磁盘;
-
innodb_flush_log_at_trx_commit=1:每提交一次事务redo log就立即刷到磁盘;
-
binlog_group_commit_sync_delay=1000:设置延迟1s后的组提交调用 fsync; delay=0的时候count就没有效果了;
-
binlog_group_commit_sync_no_delay_count:表示累积多少次以后才调用fsync刷盘;
4、小结
在集群和分布式中,一般都会涉及到CAP的问题,
主从延迟的方案:没有"银弹",需要根据具体的业务场景和问题来做平衡和取舍。
---及时当勉励,岁月不待人。
水滴石穿,积少成多。学习笔记,内容简单,用于复习,梳理巩固。
##参考资料,
《Innodb存储引擎》
《MySql实战详解》