为了庆祝EDG夺冠,学学MySQL主从同步吧

大家好,我是亚索!

“八年了,有多少场比赛是不敢拿大龙而输掉的!” 恭喜EDG八年磨一冠,在今天凌晨一点左右拿下LOL全球总决赛冠军。

实在惭愧,现在还在学校,实验室任务加上生活中各种事包括娱乐(😅),导致断更很久,这一点亚索也一日三省,如何抓紧时间,多学习,多写代码,多写分享,少手游(最近可是手游排位持续上头ing,亚索在召唤师峡谷疯狂坑人🤔)。

说起娱乐竞赛,亚索感觉到不管是娱乐还是生活学习,都会遇到各种所谓的挫折困难,保持一个良好积极乐观的心态,着重于问题的原因和根本,最终都会得到一个比较好的结果,与大家共勉。

EDG夺冠,可以像过大年一样庆祝。

凌晨一点亚索就听到有童鞋在宿舍楼大声呼喊,在网上也有看到很多人实现flag,倒立洗头,表白求爱,更夸张的还有,大街上裸奔,跳l跳河...这...

亚索觉得完全就没必要了吧,还请大家时刻保持理智和清醒,不要参与一些无意义毁三观的庆祝活动叭。


正题

闲聊stop,今天主要给大家分享一下,亚索之前学习到的MySQL分库主从同步延迟问题的解决方法

这也是后端Java面试官爱问知识之一,知识介绍均来自互联网搜集资料以及课本笔记,如有重复涉及版权问题,纯属偶然,还请私信纠正一下哈。

MySQL主从分库架构

互联网大项目都是“读多写少”的场景,如果过多“读数据”的请求落在一台数据库上,将无疑给数据库和整个系统带来不小的压力和瓶颈,所以业界普遍采用“一主多从,写主读从”的架构来提升数据库相关层面的性能,如下图所示:

读写分离具体地有代理方式、组件方式,一般首先都要做到三步:

1、部署多台数据库,选择一台作为主库,其他作为从库;

2、确保主从数据库的一致性,数据实时同步,也就是主从复制;

3、确保系统将写请求交给主数据库处理,读请求交给从数据库处理。

  • 代理方式就是在应用和数据库中间加一个代理层,所有数据请求由代理层负责处理,分离读写请求,路由到对应的数据库中。提供功能的中间件有:MySQL Router、Maxscale、MyCat、Atlas
  • 组件方式目前互联网使用较多,推荐使用shardingjdbc\color{blue}{sharding-jdbc}。直接在项目maven引入jar包即可使用。关于shardingjdbc\color{blue}{sharding-jdbc}实现读写分离可以参见官方网站 https://shardingsphere.apache.org/document/legacy/3.x/document/cn/manual/sharding-jdbc/usage/read-write-splitting/

读写分离的实现对于提升数据库的并发非常有效,对主库更新写入的数据,为了保证读取的一致性,需要及时把数据更新到从库当中去,这就是主从复制,原理以及示意图如下:

怕大家看不清图,MySQL主从复制步骤亚索再写一遍:

  • 主库更新数据后,将数据库中变化的数据写入bin log日志中,写入的是DML命令语句(Data Manipulation Language)
  • 从库连接主库
  • 从库会创建一个IO线程,向主库请求更新的bin log
  • 主库会创建一个bin log线程,来向从库发送bin log,从库的IO线程则负责接收
  • 从库的IO线程将接收到的bin log写入relay log中,从库的SQL线程读取relay log将数据同步写入本地数据库(即从库),即再执行一次DML类型的SQL语句。

主从同步延迟以及解决办法

万物有利有弊,主从分库,读写分离,会带来 \color{red}{主从同步延迟问题}

主库和从库的数据存在延迟,比如写完主库之后,主库的数据同步到从库是需要时间的,这个时间之内主库和从库的数据不一致,读取从库的数据可能就是错误的或者说旧数据。

方案一 延迟读取

简而言之就是,等主从同步延迟时间过了以后再去读取数据,只适用于数据敏感场景:

可以设计业务流程为,写请求完成之后,避免立即进行请求操作:比如你支付成功之后,跳转到一个支付成功的页面,当你点击返回之后才返回自己的账户。

方案二 强制读主库

在主从同步延迟时间内,从库数据是过期的,此时将必须读取最新数据的请求路由给主库处理,shardingjdbc\color{blue}{sharding-jdbc}可以采用这种方案,通过其HintManager分片键值管理器可以实现:

HintManager hm = HintManager.getInstance();
hm.setMasterRouterOnly();

方案三 半同步复制

等主从同步完成之后,主库上的写请求再返回,这就是“半同步复制”:

  • 系统先对主库Master进行一个写操作
  • 等主从同步完成之后,写主库的请求才返回
  • 读从库,此时读到的是最新的数据

该方案较为简单,但主库的写请求返回时间会延长,吞吐量会降低。

方案四 数据库中间件

在应用程序服务层与数据库之间加一个中间件,所有数据请求均走中间件:

  • 所有的读写都走数据库中间件,正常情况下读请求路由到从库处理,写请求路由到主库处理
  • 记录所有路由给主库的key,在主从同步延迟时间内(经验时间是500ms),如果有相关key的读请求访问中间件,就把这个key的读请求交给主库处理,因为此时从库可能还是旧的错误数据。
  • 在主从同步延迟时间过后,保持正常,所有读请求均路由到从库处理。

该方案基本能保证读取数据一致性,但数据库中间件的成本较高。

方案五 缓存记录写key法

  • 用缓存代替中间件,如果某个key要进行写操作,记录先写在缓存Cache里面,根据经验设置一个主从同步时间
  • 写主库,执行写操作
  • 读请求发生,先到缓存里查看是否有相关key的记录
  • 如果有,则为缓存命中,需要将读请求路由到主库处理
  • 如果没有命中,说明该key相关数据近期没有发生改变,继续将请求交给从库处理,继续读写分离。

小结

本文主要介绍了一下MySQL主从分库,读写分离,实现主从同步复制的步骤原理,以及相关问题的处理解决办法,有不足之处还请指出,在亚索日后的学习当中,如果有更好的方案和技术补充,会继续更新。

好了今天就说到这,我是亚索,关注我,大家一起学Java吧!

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务