「转发」数据库读写分离这个坑,你应该踩过吧?

来都来了 2020-12-11 18:22:44 ⋅ 481 阅读

作者:楼下小黑哥
原文:https://www.cnblogs.com/goodAndyxublog/p/14106692.html

Hello,大家好!我是楼下小黑哥,我又来了~

今天分享一下刚入职公司第一次发布项目遇到的一个问题,一个数据库读写分离的坑。

前言

事情是这样的,刚入职的时候接到了这样的一个业务需求:

每个支付通道支付失败的时候都会返回特定的错误码,业务内部需要将通道特定的错误码转义成内部的错误码,这样对外就可以统一返回我们自己的错误码。

这个需求其实不难,当时设计的系统架构如下:

新增规则的流程简单分为三步:

  1. 业务人员通过管理后台新增映射规则
  2. 数据库新增、修改这条映射规则
  3. 删除缓存

这里之所以增加缓存,是因为这个场景每次支付都需要使用,使用缓存可以避免每次都去数据库读取,增加读取速度。

后续支付请求业务流程如下:

当缓存内映射规则不存在的时候,将会查询数据库,然后加载到缓存中。如果缓存内映射规则已存在,将会直接使用缓存内映射规则。

这个业务流程其实比较简单,当时在测试环境测试也没问题,后续发布线上环境的却碰到奇怪的问题。

新增规则之后,一段时间内,映射规则并没有生效。查看日志发现,查询数据库的时候,没有数据。

这就很奇怪了,日志显示新增是成功,但是查询却没有数据。但是过了一段时间,再次查询却又有了数据。

走查了下代码,发现并没有什么问题,第二天上班的时候请教了一下同事,才知道问题的原因:

原来线上的数据库采用主从架构,数据读写分离,数据查询走的是从库。数据写入都是直接操作主库,后续再同步到从库。

由于数据库同步存在延时,这就导致数据同步的这段时间,主从数据将会不一致,从库无法查询到最新的数据。

如果你之前的数据库系统架构是单库或者主备结构,当你第一次转到数据读写分离架构,这个坑大概率也会踩到。

数据库系统架构发展

下面我们首先了解一下数据库系统架构,最后再来看下如何解决主从同步延时的导致数据不一致。

主备架构

业务发展的前期,数据访问量小,这时我们可以直接采用单库的架构。

不过我们一般不使用的上面的架构,因为存在单点的问题。若数据库出现故障,这段期间业务将会不可用。我们除了等待重启,其他没什么解决办法。

所以我们会增加一个备库,实时同步主库的数据。

一旦「主库」出了故障,通过人工的方式,手动的将「主机」踢下线,将「备机」改为「主机」来继续提供服务。

这种架构,部署维护简单,业务开发也无需任何改造。

不过缺点也很明显,备库只有在主库有问题的时候才会被启用,存在一定的资源浪费的情况。

主从架构

随着业务发展,请求量不断变大,数据量也不断变大,业务变得更加复杂,很快数据将会到达瓶颈。

由于大多数业务都是读多写少,所以数据库读的最容易成为系统瓶颈。

这时候我们可以提高读的性能,这时我们的可以采用的方案,增加从实例,主从同步,数据读写分离。

可以看到这个架构与主备没什么区别,主要区别在于主从架构下,从库与主库一样,时刻需要干活,主库提供写服务,从库只提供读服务。

如果后续读的压力还是太大,我们还可以增加从库的数量,水平扩充读的能力。

虽然主从架构帮我们解决读的瓶颈,但是由于主从之间需要数据同步,这天然就存在一定延时。

在这延时窗口期内,从库的读只能读到一个旧数据,这也是上面案例问题的真正的原因。

接下来我们来看下有什么办法可以优化这种情况。

主从延时解决办法

忍受大法

第一种解决办法,很简单,无他,不管他,没有读到也没事。这时业务不需要任何改造,你好,我好,她也好~

如果业务对于数据一致性要求不高,我们就可以采用这种方案。

数据同步写方案

主从数据同步方案,一般都是采用的异步方式同步给备库。

我们可以将其修改为同步方案,主从同步完成,主库上的写才能返回。

​ 

  1. 业务系统发起写操作,数据写主库
  2. 写请求需要等待主从同步完成才能返回
  3. 数据读从库,主从同步完成就能读到最新数据

这种方案,我们只需要修改数据库之间同步配置即可,业务层无需修改,相对简单。

不过,由于主库写需要等待主从完成,写请求的时延将会增加,吞吐量将会降低。

这一点对于现在在线业务,可能无法接受。

选择性强制读主

对于需要强一致的场景,我们可以将其的读请求都操作主库,这样读写都在主库,就没有不一致的情况。

这种方案业务层需要改造一下,将其强制性读主,相对改造难度较低。

不过这种方案相对于浪费了另一个数据库,增加主库的压力。

中间件选择路由法

这种方案需要使用一个中间件,所有数据库操作都先发到中间件,由中间件再分发到相应的数据库。

这时流程如下:

  1. 写请求,中间件将会发到主库,同时记录一下此时写请求的 key(操作表加主键等
  2. 读请求,如果此时 key 存在,将会路由到主库
  3. 一定时间后(经验值),中间件认为主从同步完成,删除这个 key,后续读将会读从库

这种方案,可以保持数据读写的一致。

但是系统架构增加了一个中间件,整体复杂度变高,业务开发也变得复杂,学习成本也比较高。

缓存路由大法

这种方案与中间件的方案流程比较类似,不过改造成本相对较低,不需要增加任何中间件。

这时流程如下:

  1. 写请求发往主库,同时缓存记录操作的 key,缓存的失效时间设置为主从的延时
  2. 读请求首先判断缓存是否存在
  3. 若存在,代表刚发生过写操作,读请求操作主库
  4. 若不存在,代表近期没发生写操作,读请求操作从库

这种方案相对中间件的方案成本较低,但是呢我们此时又引入一个缓存组件,所有读写之间就又多了一步缓存操作。

总结

我们引入主从架构,数据读写分离,目的是为了解决业务快速发展,请求量变大,并发量变大,从而引发的数据库的读瓶颈。

不过当引入新一个架构解决问题时,势必会带来另外一个问题,数据库读写分离之后,主从延迟从而导致数据不一致的情况。,

为了解决主从延迟,数据不一致的情况,我们可以采用以下这几种方案:

  1. 忍受大法
  2. 数据库同步写方案
  3. 选择性强制读主
  4. 中间件选择路由法
  5. 缓存路由大法

上面的方案都有各自的优点,当然也有相应的缺点,我们需要根据自己的业务情况,选择相应的解决方案。

 


全部评论: 0

    我有话说:

    8 种最的 SQL 错误用法,有没有

    编写复杂SQL语句要养成使用 WITH 语句的习惯。简洁且思路清晰的SQL语句也能减小数据库的负担 。

    一文看懂mycat配置--数据库分离、分表分库

    波波说运维https://www.toutiao.com/i6742436467806568973 概述 系统开发中,数据库是非常重要的一个点。除了程序的本身的优化,如:SQL语句优化、代码优化

    应该避免的五个简单的数据库设计错误

    Anith 在他非常成功的文章 Facts and Fallacies about First Normal Form 之后,对五个常见的数据库设计错误进行了引人入胜的讨论,尽管使用它们的不幸后果

    为什么说作为程序员分库分表的必要性一定要掌握?

      互联网大厂程序员必须掌握海量数据和高并发问题处理技能,期望进入大厂的程序员一定要仔细看这篇! MySQL 分库分表是做什么的? 相信很多程序员对 MySQL 都比较熟悉了,目前国内

    纯JS实现复制功能的三种方式,有

    业余时间了一个在WX里面分享X宝优惠券的小工具,里面有用到复制T口令的功能,当时以为实现起来很....

    Dgraph 1.2.8 发布,事务性分布式图形数据库

    Dgraph 1.2.8 发布了。Dgraph 是一个可扩展的,分布式的,低延迟的图数据库,目标是提供 Google 生产水平的规模和吞吐量,在超过 TB 的结构数据里,为用户提供足够低延迟的实时

    什么情况下才需要分库分表?

    我请教一下,我模拟测试,搞了几张大表都是3千万左右的数据,带主键查询秒级响应,jmeter插入能够达到每秒6、700条左右,我业务量每秒就是300条左右插入,这个场景下是否不需要考虑分库分表的问题?

    推荐一款前端数据源管理工具 algeb

    ALGEB 简介 这是一个比较抽象的库,一开始可能比较难理解。我它的初衷,是创建可响应的数据请求管理。在传统数据请求中,我们只是把携带ajax代码的一堆函数放在一起,这样就可以调用接口。但是这种

    搞对数据库连接池,这次从100优化到3ms!阿里架构师都说好

    做译文分享。 接下来是正文 数据库连接池的配置是...

    分库分表这样玩,可以永不迁移数据、避免热点

    中大型项目中,一旦遇到数据量比较大,小伙伴应该都知道就应该数据进行拆分了。有垂直和水平两种。

    【SpringCloud实战】一次开发中使用Feign添加动态Header问题思考

    了一个Spring Cloud Feign添加自定义Header的分享给大家

    「轻阅读」“来我公司做技术总监” “要代码吗?” “不代码来干嘛?”

    标题来源于一段真实的对话,老赵,小李都是我的朋友,我作为中间人介绍他们认识,我们约在上海码农圣地--张江某咖啡馆。

    MySQL 插入 100万 条数据整理笔记

    多线程插入(单表) 问:为何对同一个表的插入多线程会比单线程快?同一时间对一个表的操作不应该是独占的吗? 答:在数据里做插入操作的时候,整体时间的分配这样的: 1、多链接耗时 (30

    数据结构

    结构,简单的理解就是关系。严格点说,结构是指各个组成部分相互搭配和排列的方式。在现实世界中,不同数据元素之间不是独立的,而是存在特定的关系,我们将这些关系成为结构。 数据结构:是相互之间存在一种

    转载」47 张图带 MySQL 进阶!!!

    我们在  138 张图带 MySQL 入门 中主要介绍了基本的 SQL 命令、数据类型和函数,在具备以上知识后,就可以进行 MySQL 的开发工作了,但是如果要成为一个合格的开发人员

    想更好的理解Node.js中的Buffer吗?看一下这个。

    不论是否是科班出身,认真完,想必会给带去一些收获.

    前端实战篇-聊聊JavaScript内存

    内存生命周期、分配内存、使用分配的内存(操作),当应用程序不再需要时,释放掉已分配的内存

    京东到家订单中心系统mysql到es的转化之路

    大,造成了订单数据少的情况。 我们把订单数...

    10分钟看懂,Java NIO 底层原理

    目录 在前面 1.1. Java IO原理 1.1.1. 内核缓冲与进程缓冲区 1.1.2. java IO的底层流程 1.2. 四种主要的IO模型 1.3