主从复制
- 概念:在主从架构中必须有一个主节点,以及一个或多个从节点,所有数据会先写入主节点,之后再同步到从节点中;
- 带来的优势:
- 高可用:在主节点宕机或故障时,从节点可以自动切换成主节点,继续对外提供服务;
- 数据稳定:从节点上保存着全量数据,当主节点数据损坏时,可从从节点中恢复;
- 提升性能:可以基于主从架构实现读写分离,主节点处理写请求,从节点处理读请求,从而提升性能;
- 存在的问题:
- 存在木桶效应,主从集群的节点容量受限与存储容量最低的那台机器;
- 数据一致性问题:主从直接数据同步一般通过网络传输完成,存在一个定的延迟;
- 脑裂问题:主从节点之间一般会通过心跳机制来判断节点存活状态,在网络故障的情况下可能会产生多个主节点共存的情况,从而导致数据丢失;
Mysql的主从复制技术
数据同步原理
- 实现原理:Mysql主从之间的数据复制是基于日志
Bin-log和Relay-log
实现的; - 数据同步的两种方式:
- 主节点推送:当主节点出现数据变更时,向所有的从节点推送数据;
- 从节点拉取:从节点定期询问主节点是否有数据更新,当存在数据变更时,主动拉取新数据;
- mysql数据同步方式:采用从节点拉取的方式同步数据,但将传统方式需要主从节点之间保持长连接,从节点定时或持续性的对主节点做轮询的方式,改为主节点发送通知,从节点监听通知的方式;
- 具体流程:
- 主节点接收到数据写入请求后,先向自身写入数据,并记录
Bin-log
日志; - 在配置主从架构后,主节点上会创建一条
log dump
线程,专门监听Bin-log
日志,当监听到Bin-log
日志变化时,会发起dump
请求,通知从节点来拉取数据; - 从节点有专门的
I/O
线程用于等待主节点通知,当收到通知后会去请求一定范围的数据; - 当从节点在主节点获取到需要同步的数据后,会写入
Relay-log
中继日志中; - 从节点上负责监听
Relay-log
变更的SQL线程在监听到日志变更后,会读取日志中的记录; - 解析读取到的日志记录,并将解析出的数据写入磁盘中;
- 同步数据的格式:
- 由上述流程可知,主从同步主要是同步主节点的
Bin-log
日志,所以同步数据格式是由主节点的Bin-log
日志决定,而Bin-log
有三种格式,一般会采用Mixed
格式(具体可参考日志);
主从复制数据的方式
Mysql一共支持同步复制、异步复制、半同步复制、增强式半同步复制/无损复制,四种数据同步方式;
同步复制
- 主节点数据写入完成后,从节点会同步主节点写入的数据,当从节点同步完成后再有主节点返回响应,保证了数据的强一致性;
异步复制
- 数据库默认采用的方式,主节点数据写入完成后直接返回响应,从节点会在其他时间进行数据同步,提高了请求响应速度;
半同步复制
- 概念:当写请求到达主节点后,会先在主节点执行,执行完成后会向所有的从节点发送数据同步请求,待有一个从节点写入成功并返回
ACK
则向客户端返回写入成功,且为了避免网络延迟导致主节点长时间接受不到从库响应,因此会有rpl_semi_sync_master_timeout
参数来控制超时时间,默认为10000ms/10s
,若超出设定时间还未收到从库的响应,则会将复制模式切换成异步模式,会在后续网络正常后再次切回半同步模式; -
Zookeeper
的半同步复制:这种有一个从节点写入成功并返回ACK
则向客户端返回写入成功的方式任然存在数据丢失风险,所以Zookeeper
中需要主节点收到一半从节点以上的ACK
时才会向客户端返回数据写入成功,具体是通过Zookeeper
的一致性协议ZAB
保证的;
增强式半同步复制/无损复制
-
Mysql5.7
引入,覆盖了之前的普通半同步模式; - 与半同步复制的区别:主要是对主库事务提交的时机做了修改;
- 普通半同步复制,采用的是
after-commit
模式,也就是在主库未收到从库的ACK
请求之前,虽然不会向客户端返回响应,但是会提交事物,也就是主库中的后续事物是可以看到对应的数据的,此时主库上有数据从库上没有,若此时宕机会导致,旧主库与新主库数据不一致的情况; - 增强式半同步复制,采用的是
after-sync
模式,也就是在主库未收到从库的ACK
请求时,不会在主库上提交事物,保证了主从节点的数据强一致性,但这种方式事物可能会长时间不提交,会导致对应的锁资源不会主动释放,从而可能导致主库的整体性能下降;
Mysql5.6中的复制特性
延迟复制:
- 概念:它可以支持从库数据延迟同步,也就是当从库上的
I/O
现场,将主库的Bin-log
日志请求回来后,从节点SQL
线程不会立刻解析日志执行,而是会等待一段时间再去解析执行,等待时间可以配置; - 好处:
- 可以一定程度的防止主节点误操作数据、表、库或其他数据库对象,可及时通过从节点上的数据恢复数据;
- 能对一些线上Bug进行实时观测,比如一个无法复现的故障问题发生时,如果发现时还在配置的延迟复制时间内,则可以去到从库上观察;
并行复制:
GTID(Global Transaction ID)
复制:GTID(Global Transaction ID)
也就是全局事务标识符的意思,它由节点UUID+事务ID
两部分组成,MySQL在第一次启动时都会利用UUID
随机生成一个server_id
,而MySQL会对每一个写事务都分配一个顺序递增的值作为事务ID,而GTID则是由这两个组成的,格式为server_uuid:trx_id
;
- 此时在在主从发生切换时,不需要再去手动指定
Bin-log
的POS
同步点,只需要执行change master to master_auto_position = 1
这条命令即可,也就是使Mysql
具备自动断点复制的功能; - 执行流程:
-
master
在更新数据时,会为每一个写事务分配一个全局的GTID
,并记录到Bin-log
中。 -
slave
节点的I/O
线程拉取数据时,会将读到的记录写到relay-log
中,并设置gtid_next
值。 -
slave
节点的SQL
线程执行前,会读取gtid_next
值得知接下来该解析哪条日志并执行。 slave
节点的SQL
线程在执行时,会先比对自身的Bin-log
日志中是否有对应的GTID
:
- 有:意味着该
GTID
对应的事务已经执行过了,slave
会自动忽略掉这条记录。 - 没有:
SQL
解析该GTID
对应的relay-log
记录并执行,再将GTID
记录到Bin-log
。
- 主从切换时自动寻找同步点的原理:开启
GTID
后,若发生了主从切换,假设这时主从集群中有多个从节点,MySQL
首先会选择距离master
的GTID
最近的从节点作为新主,然后将其他从节点转变为新主的从库,其他从库会根据自身gtid_next
值,去新主的日志文件中做对比,然后找到各自的同步点,继续从新主中复制数据;
- 组复制:将一组并行执行的事物,全部放到一个
GTID
中记录,后续从节点同步时,会一次性读取这一组事物来解析执行,与传统的GTID
由节点server_id
+事物ID
组成不同,组复制的GTID
通过逗号分隔:12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89, 12EEA4RD6-45AC-667B-33DD-CCC55EF718D:89-94, ......
- 它是在事物组提交时生成;
- 并行复制:
-
MySQL5.6
版本中,是基于库级别的并行复制,也就是一个从节点对应多个主节点时,有几个主节点就开几条SQL线程去解析并写入数据,即多主一从架构中才会用到;
原因是需要考虑锁冲突和并发执行时的顺序问题,比如主库上对于一条数据是先改后删,从库在并发执行时,因为多线程执行的无序性,把执行顺序改为了先删后改,这显然就会导致数据冲突,这也导致在MySQL5.6
版本并行复制中十分鸡肋; -
MTS(enhanced multi-threaded slave)
机制:MySQL5.7
版本中才基于组复制技术实现真正意义上的并行复制,因为能够在同一时间内提交的事物是不存在锁冲突的,总的来说就是,主库上是如何写入并发数据的,从库上也会开启对应的线程去并发写入; -
MySQL8.0
版本中又对MTS
进一步优化,也就是基于writeset
的MTS
技术,解决新的从节点加入集群时,需要从头开始同步数据的性能问题;
解决原理是,多个事物之间,只要变更的数据记录没有重叠,说明操作的数据没有冲突,就也可以并发执行,无需在一个事务组内; - 意义:提升从库复制数据的速度,尤其是在同步、半同步或者无损复制模式中主节点需等待从节点
ACK
响应的情况下,在一定程度上减小主从库之间的数据一致性问题;
主从数据一致性问题
通常来说,对MYSQL
搭建主从集群,都会在主从热备的情况下,采用读写分离方案,主库负责处理写请求,从库负责处理读请求,而在进行主从同步需要一定的时间,也就导致主库和从库数据会存在不一致的情况,也就是说当用户在进行数据修改后,再次查询数据,可能获取到的依染然是旧数据,导致用户认为修改失败,影响用户体验;
解决方案
- 改变业务逻辑:对业务流程进行一定的更改,如增加一个审核状态,这样可以给用户一个确认的反馈;
- 适用于一些能接受一定数据延迟,对数据实时性要求不高的场景;
- 更改复制方式:全同步复制可以保证数据之间不存在延迟,半同步模式复制可以降低数据不一致出现概率;
- 适用于对实时性要求较高的场景;
- 调整数据库架构:比如升级服务器,将集群架构恢复为单体架构,或者采用分库分表的方案替代集群方案;
- 引入第三方中间件:如引入
Canal
中间件来监控主节点的Bin-log
日志,Canal
在主从集群的身份就类似于一个中间商,对于主节点而言,它是一个从库,因为Canal
会去主库上拉取新增数据。而对于集群中的从节点而言,它是一个主库,因为Canal
会给其他真正的从节点推送数据;虽然Canal
在监听变更、推送数据环节都需要一定的时间,但会比从库去主库上拉取的速度更快一些;