原因解析:主从库之间需要通过日志的方式进行数据同步,如果此时用户的读请求交给从库去处理,一旦数据同步操作未完成,则用户此时读到的数据是旧数据,会导致用户获取数据不可靠,影响业务的正常运行和用户体验。

解决办法:

方法1:设置数据库主从半同步(全同步)

三种同步复制方式比较
全同步半同步异步
主库在执行完客户端提交的事务后 ,所有从库已经接收并处理完成主库在执行客户端提交的事务后,至少等到一个从库接收完成(而不是处理完成)后再将结果返回给客户端主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理
安全性最高,延迟最高居中延迟最低,安全性差

 

由此可知,选择一个合理的主从库数据同步复制方法很重要,以mysql为例,MySQL的Replication默认是一个异步复制的过程,从MySQL5.5开始,MySQL以插件的形式支持半同步复制,确保事务提交后binlog至少传输到一个从库但是不保证从库应用完成这个事务的binlog,性能有一定的降低,网络异常或从库宕机,卡主库,直到超时或从库恢复。
该方案优点:利用数据库原生功能,比较简单

该方案缺点:主库的写请求时延会增长,吞吐量会降低

方法2:强制读主,不使用从库,而且是架构上增加内存数据库

服务器的读写数据其实最终都是从主库中获取数据,只是将一部分的高热数据保存在前置的内存数据库(redis,memached,mongodb等),以服务器的内存开销来处理读请求,降低主数据库的压力。

读写的基本流程:

读操作:先进到缓存数据库,有直接读,没有回主库读,并在缓存数据库中保存

写操作:删除缓存数据库中已有的数据,直接写入到主库

修改操作:修改到主库,删除缓存数据库中已有的数据

注意:使用强制读主,需要进行系统架构改造(去掉从库)

方案优点:“一致性”上不需要进行系统改造

方案缺点:只能通过cache来提升系统的读性能,这里要进行系统改造

方法3:增加数据库中间件来解决

流程:

1)所有的读写都走数据库中间件,通常情况下,写请求路由到主库,读请求路由到从库

2)记录所有路由到写库的key,在主从同步时间窗口内(假设是500ms),如果有读请求访问中间件,此时有可能从库还是旧数据,就把这个key上的读请求路由到主库。

3)在主从同步时间过完后,对应key的读请求继续路由到从库。

阿里巴巴数据库中间件
canalotter
是阿里巴巴旗下的一款开源项目,纯Java开发,基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL。也是阿里开源的一个分布式数据库同步系统,尤其是在跨机房数据库同步方面,有很强大的功能。它是基于数据库增量日志解析,实时将数据同步到本机房或跨机房的mysql/oracle数据库。
 otter目前嵌入式依赖canal,部署为同一个jvm,目前设计为不产生Relay Log。otter目前允许自定义同步逻辑,解决各类需求。

方案优点:能保证绝对一致

方案缺点:数据库中间件的成本比较高

方法4:缓存记录写key法

既然数据库中间件的成本比较高,有没有更低成本的方案来记录某一个库的某一个key上发生了写请求呢?很容易想到使用缓存,当写请求发生的时候:

方案优点:相对数据库中间件,成本较低

方案缺点:为了保证“一致性”,引入了一个cache组件,并且读写数据库时都多了一步cache操作

 

 

为了解决主从数据库读取旧数据的问题,常用的方案有四种:

(1)半同步复制

(2)强制读主

(3)数据库中间件

(4)缓存记录写key

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐