MySQL - 主从
主从同步
主从同步的过程如下图所示,核心就是,当从库连接上主库之后,主库为为这个从库创建一个dump线程,用于传输binlog。从库有一个IO线程来接收binlog并写入它的中转日志relay log,同时有一个SQL线程读取relay log进行执行。主从同步保证的是数据的最终一致性。主从同步的方式包括:
- 异步复制,主库事务完成就立刻返回客户端,不关心是否复制到从库上 (默认采取异步复制)
- 同步复制,主库事务完成后要等待所有从库都复制了这个事务,才返回成功给客户端
- 半同步复制,主库事务完成后,至少一个从库接收并写到relay log才返回成功给客户端
主从同步什么时候pull,什么时候push?答案是都有。当slave连上master,会告知master它目前的binlog fileName和同步位点pos, master收到后,会找到该文件并把剩下的binlog发给slave,这其实就是pull。当连接进行时,master侧有新的binlog产生时,主线程会广播dump thread有新binlog产生,然后再发给slave,也就是push。
主备延迟
主备延迟的定义:同一个事务,在主库执行完成到从库执行完成的这个时间差。根据主从同步的过程,我们可以把过程切割成3个时间点:
- 主库写入binlog的时间点t1
- 从库接收到binlog的时间点t2
- 从库写入数据更新的时间点t3
t1其实就是事务完成的时间(算主备延迟的起始时间点);t2-t1主要就是网络开销,正常情况下是很小的。所以主备延迟主要是来自于t3-t2即从库执行relay log的时间/消费中转日志的时间。
导致t3-t2时间较大的原因,有以下几点:
- 从库的规格/机器性能比主库差
- 从库的压力比较大。例如,放了很多运营查询/分析的语句到从库执行。这种情况解决的办法有:一主多从,或者将binlog输出到外部系统如hadoop,来提供统计查询等能力,核心就是分担从库压力
- 大事务。假设一个事务在主库执行5分钟,那么在从库也会延迟5分钟。
主备一致
那么在主从同步的过程会不会出现主备数据不一致的问题呢?其实是有可能的。在日志模块,我们提到,binlog_format其实根本上有两种,一种是statement(SQL语句本身),一种是row(影响修改的行信息和事件)。
例如日志模块提到的这个栗子🌰:
delete from t where code >= 1 and create_time <= '2022-2-16' limit 1;
如果主库从库选择的索引不一样,就会导致最后执行结果的不一样,也解释了为什么binlog_format=statement
时,这句话会出现warning。 当然如果binlog_format=row
是不会出现这个问题的。
第二个栗子🌰:
insert into t values(10, 10, now());
如果binlog_format=statement
,且直接手动从binlog解析出来执行,忽略event的上下文,也会出现主备不一致。正常来说,该上下文会记录这个timestamp,而不会出现主备不一致。
主从结构
一主多从 MySQL Replication/MySQL Fabric(官方提供)
MySQL Fabric在Replication的基础上,提供故障检测和转移,即主节点挂了,能从从节点选择一个来当主节点
- 优点:
👍一主多从相对于一个mysql来说,可以实现读写分离,从库用来读,主库用来写,通过读从库的负载均衡分担压力
👍可以让某个从库停止复制进程来备份数据而不产生影响即不影响对外服务。(当然,对于InnoDB引擎来说,可以使用--single-transaction
参数来在备份数据时开启一个事务,获取一致性备份,而不阻塞读写) - 缺点:
👎单一的主节点挂了,就不能提供写服务了
👎主备延迟
双主 MMM
Master Replication Manager for MySQL是谷歌的开源项目,用来支持双主故障切换。
- 优点:
👍除了具备一主多从的读写分离负载均衡等优点以外,最重要的就是自动的主主Failover切换。 - 缺点:
👎无法保证数据一致性,主1挂了,主2没来得及完成复制,成为主节点,数据不一致
👎(todo 不太明白)由于是使用虚拟IP浮动技术,类似Keepalived,故RIP(真实IP)要和VIP(虚拟IP)在同一网段。如果是在不同网段也可以,需要用到虚拟路由技术。但是绝对要在同一个IDC机房,不可跨IDC机房组建集群
多主多从 MySQL Cluster(官方提供)
这里暂时不做了解啦(todo)
主从切换
MySQL如何保证高可用?主从切换有两个策略,一个是可靠性优先,一个是可用性优先。
- 可靠性优先:
1 判断备库B的seconds_behind_master
(SBM), 如果小于某个值如5s,进行下一步,否则持续重试
2 暂停主库A的所有写,即改为只读
3 判断备库B的seconds_behind_master
(SBM),直到0为止
4 备库B从只读改为可读写
5 业务请求切换到B - 可用性优先:
将上述步骤4,5放到最开始执行,代价就是数据可能不一致
遗留项
1 主从同步时的从节点并行复制策略 MySQL 5.6,5.7,...
2 主从切换中的GTID (Global Transaction Identifier)
HAProxy + Keepalived
todo