Mysql主从同步延迟解决方案

Mysql的主-备-从部署架构是经典方案,主库执行写操作,从库分担读压力。但是也会产生主从数据不一致的问题,从库拉取主库binlog同步数据, 必定存在延迟,从客户端来看,总是希望写入之后立即能读取最新数据。否则会造成比如用户支付成功后查看订单详情还是未支付状态。

1. 强制查询走主库

这种方案需要在业务层面区分出哪些查询必须读取实时数据,哪些查询可以接受延迟。对于不能接受延迟的查询,强制路由到主库,这种方案属于大力出奇迹, 能够保证读到最新数据,但是如果从业务层面权衡,比如交易场景下,可能会发现所有的查询都要求实时,这样读写分离就名存实亡了。
其实,在实际生产中,碰到过数据延迟的问题,解决方案就是修改路由强制走了主库,这种方案足够简单,而且数据库层面在设计时也有性能的冗余,问题就 这样简单的解决了。

2. 查询前sleep方案

这种方案看起来很傻,我们悲观的认为一定会出现延迟,因此在查询之前固定给一个延迟时间,这样等这段时间数据同步了再查询,但是这样没有考虑当前的 主从同步延迟情况,一刀切给一个延迟,在高并发系统,接口都加一个延迟,可能引起更大的性能问题。

3. 根据当前延迟情况决定查询路由

这种方案相比2的一刀切要合理些,首先看看当前从库的数据同步状态,然后根据同步完成与否确定是走从库还是主库。具体判断是否有同步延迟可以基于seconds_behind_master 、对比同步位点、对比gtid集合等方法。但是还存在一个问题,上面这些方式判断是否有延迟,是基于从库收到的binlog是否执行完了,那么就存在虽然当前 主库同步过来的日志都执行完了,但是还有主库已经提交事务但日志还没有拉过来的情况,因此也不是万无一失的。

4. 半同步复制+延迟判断

如果binlog是异步复制,那么主库提交事务完成请求就返回了,如果是半同步复制semi-sync,大致流程就是:

  • 事务提交的时候,主库把 binlog 发给从库;
  • 从库收到 binlog 以后,发回给主库一个 ack,表示收到了;
  • 主库收到这个 ack 以后,才能给客户端返回“事务完成”的确认。 这样就能保证事务执行成功,日志一定到了从库,再结合同步位点判断,就能保证数据一定是同步了。但是还有一个问题,我们一般的部署方案是一主多从, 那么只要一个从库保证收到日志,事务就提交了,但是其他从库查询依然是有延迟的。

5. 等主库位点/GTID

在执行事务后需要立即查询到最新数据的场景下,我们需要确保只有刚才执行的这个事务已经在从库执行了,才能放心大胆地用从库数据,因此可以记录下主库这个事务 的信息,查从库前先确保这个事务已经同步过来了。流程如下:

  • 事务更新完成后,马上执行 show master status 得到当前主库执行到的 File 和 Position;
  • 选定一个从库执行查询语,从库上执行 select master_pos_wait(File, Position, 1);
  • 如果返回值是 >=0 的正整数,则在这个从库执行查询语句,否则,到主库执行查询语句。

6. 其他

这个问题的产生就是因为一主多从读写分离造成的,在这种部署架构下,这个问题必然存在,因此,我们需要从更宏观的视角来看这个问题,不要一条路走到黑。 比如,分库分表将读写压力分散,这样每个分库都承担一部分数据的读写。仔细分析所有的读请求,有很多分析型的数据查询通过ES或者其他离线数据处理平台处理, 进一步降低读压力。同时在业务设计上,充分考虑读延迟的情况,比如在支付完成后进入一个中间页面或者加个loading,实际上,生产环境中我们经常通过这些 曲线方案解决。

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @sxzhou May 19, 2021

奉献爱心