背景
在如今高并发的大背景下,单机数据库已无法满足需求,因此很多公司采用集群部署模式,其中一主多从是最常见的架构,不仅实施简单还可以能实现高可用、读写分离(一个主库写多个从库读,因为读多写少),进而提升集群的并发能力。
影响
本周出现线上报警,kafka消息发送失败。究其原因,发现是代码里没有根据刚插入的数据Id查询出数据库记录,导致生产者消息为空,广播数据失败。
那么问题来了,数据一定写入成功的情况下,偶现的主从延迟应该如何解决呢?
解决
1.强制读主库
好处:保证一定能读到数据且读到的数据是最新的。
坏处:只适应于并发量不大的场景,否则就失去了读写分离意义。
实现:查询开事物,自动读主库。
2.读数据时先sleep一下
好处:逻辑简单,易操作。
坏处:降低系统吞吐量。
3.发生主从延迟时自旋(推荐)
好处:针对偶现的主从延迟情况做处理。
实现:当从数据库读到数据为空时,for循环3次,每次等待0.5S,如果还没查出来则报警。
4.检查代码问题
如果是Kafka消费的场景可以考虑延时消费。
写入数据再读出来,这个过程不能放同一个事务里,否则永远读不到。
避免写入立即查询。写完库后把当前实体的数据带入后面流程,避免再次查库。
5.MySQL服务端本身的优化
MySQL 5.6 版本后,提供了一种并行复制的方式,通过将 SQL 线程转换为多个 work 线程来进行重放,这样就解决了主从延迟的问题
趁着并行复制机制,如果面试官再深入问你一个问题:
主库突然宕机,此时恰好数据还没同步到从库,那么有些数据可能在从库上是没有的,导致数据丢失,MYSQL如何处理这种情况?
答:MySQL有半同步复制机制,就是主库写入 binlog 日志之后,就会将强制此时立即将数据同步到从库,从库将日志写入自己本地的 relay log 之后,接着会返回一个 ack 给主库,主库接收到至少一个从库的 ack 之后才会认为写操作完成了。
MYSQL本身采用半同步复制来解决主库数据丢失问题;并行复制来解决主从同步延时问题。
刨根问底
主从同步原理
两个关键log:
- bin log(二进制日志文件)
- relay log(中继日志文件)
两个关键线程:
- I/O线程
- SQL线程
主从架构配置完成后
从库自动生成I/O线程和SQL线程,用于接收主库binlog和处理relay log。
从库的I/O线程请求主库binlog,主库开一个后台线程传输binglog。
从库将接收到的binglog转换为relay log
从库的SQL线程解析relay log成Mysql操作,完成主从数据同步。
同步延迟原因
主库写binlog操作是顺序I/O,从库重放操作是随机I/O
主库对所有DDL和DML产生的日志写进binlog,由于binlog是顺序写,所以效率很高。Slave的SQL Thread线程将主库的DDL和DML操作事件在slave中重放。DML和DDL的IO操作是随机的,不是顺序的,成本高很多。所以SQL Thread线程的速度赶不上主库学binlog的速度,就会产生主从延迟。
命令查看同步状态
登陆主库,输入show slave status
,查看 Seconds_Behind_Master
参数。