一、 主从原理
原生来说MySQL的主从主要是有三个线程,当然基于MySQL做的分布式数据库在数据节点上也是这三类节点。原生的三个线程是串行的,分布式下则改为了异步,这三个线程及作用如下:
1、dump线程
运行主节点上,响应从库的I/O线程,根据从库I/O线程的请求位置将主库对应的二进制日志(binlog)数据变更事件(如DDL、DML语句,这些事件记录了主库的数据更新操作)发送到从库;在读取binlog内容时,该线程会对主库的binlog加锁,以保证数据读取的一致性;读取完成后(甚至在发送给从库之前)锁会被释放,避免长期阻塞主库写操作。
2、I/O线程
运行在从库上,当从库执行start slave命令时,I/O线程被创建并负责与主库建立连接,维持主从间的网络通信;持续监听并请求主库的binlog更新,根据主库反馈的最新日志位置,获取指定范围的binlog事件内容,将拉取到的主库binlog事件写入从库本地的中继日志文件(relay log),作为从库数据同步的临时存储中转。
3、SQL线程
SQL线程持续监控中继日志文件,从中读取主库传递过来的数据变更事件(如INSERT、UPDATE、DELETE等操作);将读取到的事件解析成具体的数据操作,并严格按照事务在主库提交的顺序执行,确保数据变更的准确性和顺序性。
以上是MySQL主从下的三个线程及其作用,整体而言MySQL的主从工作流程为 :
主库数据更新后,dump线程读取binlog(加锁→读取→释放锁)→发送事件给从库I/O线程→从库写入中继日志(relay log)→从库SQL线程执行事件,最终完成主从数据一致
二、 同步方式
在MySQL中主从同步方式其实主要是两类:异步复制和半同步复制,其中半同步复制又分为after sync和after commit两类。
异步复制很好理解,就是主节点只负责根据请求的信息将binlog发送给备库,至于备库是否有接收到主库不管,主库只负责发送(和Oracle中DataGuard下的最大性能模式类似),在实际应用异步复制反而较少(当然是指在本人的经历中很少见到纯异步复制)。
半同步复制是指在主库将binlog发送到备库后,主库需要等备库做ack应答,主库在收到应答后才会提交事务,在一主多从下一般来说是只要有一个应答主库就可以提交事务(rpl_semi_sync_master_wait_for_slave_count 这个参数指定了主库在提交事务之前需要等待的最小从库数量,默认为1)。after sync和after commit的区别就在于应答的时机。after sync 是在主库innodb引擎提交事务之前备库给出应答,在分布式下强一致性也多用于该模式;after commit则是在主库收到应答时其实主库innodb已经提交了事务,在该模式下其实是存在备库还没有应答,但是主库已经可以看到提交后的事务,因此该模式下在备库还没有收到binlog时发生主从切换,备库会接收到不到主库上最新的binlog导致主从数据不一致。
半同步模式下既然主库需要收到应答才会提交事务,尤其是after sync模式,那么备库是在什么时候应答,是不是如果主库没有收到应答就永远不会提交呢?
第一个问题,备库其实是在I/O线程阶段就给出应答了,当I/O线程将事件写入到中继日志中时就会向主库做出ACK应答。
第二个问题,这个根据参数设置:sync_binlog和binlog_checksum。sync_binlog = 1 和 binlog_checksum = CRC32 会确保主库等待从库应答,不会退化为异步;sync_binlog > 1 或 0 会允许主库在从库没有应答时继续处理事务,可能会退化为异步。rpl_semi_sync_master_timeout(单位是毫秒)决定了在半同步模式下主库等待备库应答的时间,如果在设置的时间内没有收到任一备库应答则会退化为异步,在主从恢复后会自动恢复半同步模式。
半同步模式下可以看出是可以退化为异步的,那么从库是怎么确定自己是否需要给主库应答呢,尤其是在退化期间?
这个答案就要先说主从复制的单位是什么,一个binlog文件还是一个事务呢?其实都不是。主从复制的基本单元是event(事件),原因是Binlog的定义是由一个个单一的Event组成的序列。在主从中主库提交事务的flush阶段这里会判断是否开启了半同步模式,然后在从库I/O线程写入中继日志后也会判断是否开启了半同步模式,以及当前event的header中ACK标记是否为1,从而来决定是否需要给主库回复ACK。