数据库读写分离这个坑,你应该踩过吧?
Hello,大家好!我是楼下小黑哥,我又来了~
今天分享一下刚入职公司第一次发布项目遇到的一个问题,一个数据库读写分离的坑。
前言
事情是这样的,刚入职的时候接到了这样的一个业务需求:
每个支付通道支付失败的时候都会返回特定的错误码,业务内部需要将通道特定的错误码转义成内部的错误码,这样对外就可以统一返回我们自己的错误码。
这个需求其实不难,当时设计的系统架构如下:
新增规则的流程简单分为三步:
- 业务人员通过管理后台新增映射规则
- 数据库新增、修改这条映射规则
- 删除缓存
这里之所以增加缓存,是因为这个场景每次支付都需要使用,使用缓存可以避免每次都去数据库读取,增加读取速度。
后续支付请求业务流程如下:
当缓存内映射规则不存在的时候,将会查询数据库,然后加载到缓存中。如果缓存内映射规则已存在,将会直接使用缓存内映射规则。
这个业务流程其实比较简单,当时在测试环境测试也没问题,后续发布线上环境的却碰到奇怪的问题。
新增规则之后,一段时间内,映射规则并没有生效。查看日志发现,查询数据库的时候,没有数据。
这就很奇怪了,日志显示新增是成功,但是查询却没有数据。但是过了一段时间,再次查询却又有了数据。
走查了下代码,发现并没有什么问题,第二天上班的时候请教了一下同事,才知道问题的原因:
原来线上的数据库采用主从架构,数据读写分离,数据查询走的是从库。数据写入都是直接操作主库,后续再同步到从库。
由于数据库同步存在延时,这就导致数据同步的这段时间,主从数据将会不一致,从库无法查询到最新的数据。
如果你之前的数据库系统架构是单库或者主备结构,当你第一次转到数据读写分离架构,这个坑大概率也会踩到。
数据库系统架构发展
下面我们首先了解一下数据库系统架构,最后再来看下如何解决主从同步延时的导致数据不一致。
主备架构
业务发展的前期,数据访问量小,这时我们可以直接采用单库的架构。
不过我们一般不使用的上面的架构,因为存在单点的问题。若数据库出现故障,这段期间业务将会不可用。我们除了等待重启,其他没什么解决办法。
所以我们会增加一个备库,实时同步主库的数据。
一旦「主库」出了故障,通过人工的方式,手动的将「主机」踢下线,将「备机」改为「主机」来继续提供服务。
这种架构,部署维护简单,业务开发也无需任何改造。
不过缺点也很明显,备库只有在主库有问题的时候才会被启用,存在一定的资源浪费的情况。
主从架构
随着业务发展,请求量不断变大,数据量也不断变大,业务变得更加复杂,很快数据将会到达瓶颈。
由于大多数业务都是读多写少,所以数据库读的最容易成为系统瓶颈。
这时候我们可以提高读的性能,这时我们的可以采用的方案,增加从实例,主从同步,数据读写分离。
可以看到这个架构与主备没什么区别,主要区别在于主从架构下,从库与主库一样,时刻需要干活,主库提供写服务,从库只提供读服务。
如果后续读的压力还是太大,我们还可以增加从库的数量,水平扩充读的能力。
虽然主从架构帮我们解决读的瓶颈,但是由于主从之间需要数据同步,这天然就存在一定延时。
在这延时窗口期内,从库的读只能读到一个旧数据,这也是上面案例问题的真正的原因。
接下来我们来看下有什么办法可以优化这种情况。
主从延时解决办法
忍受大法
第一种解决办法,很简单,无他,不管他,没有读到也没事。这时业务不需要任何改造,你好,我好,她也好~
如果业务对于数据一致性要求不高,我们就可以采用这种方案。
数据同步写方案
主从数据同步方案,一般都是采用的异步方式同步给备库。
我们可以将其修改为同步方案,主从同步完成,主库上的写才能返回。
- 业务系统发起写操作,数据写主库
- 写请求需要等待主从同步完成才能返回
- 数据读从库,主从同步完成就能读到最新数据
这种方案,我们只需要修改数据库之间同步配置即可,业务层无需修改,相对简单。
不过,由于主库写需要等待主从完成,写请求的时延将会增加,吞吐量将会降低。
这一点对于现在在线业务,可能无法接受。
选择性强制读主
对于需要强一致的场景,我们可以将其的读请求都操作主库,这样读写都在主库,就没有不一致的情况。
这种方案业务层需要改造一下,将其强制性读主,相对改造难度较低。
不过这种方案相对于浪费了另一个数据库,增加主库的压力。
中间件选择路由法
这种方案需要使用一个中间件,所有数据库操作都先发到中间件,由中间件再分发到相应的数据库。
这时流程如下:
- 写请求,中间件将会发到主库,同时记录一下此时写请求的 key(操作表加主键等)
- 读请求,如果此时 key 存在,将会路由到主库
- 一定时间后(经验值),中间件认为主从同步完成,删除这个 key,后续读将会读从库
这种方案,可以保持数据读写的一致。
但是系统架构增加了一个中间件,整体复杂度变高,业务开发也变得复杂,学习成本也比较高。
缓存路由大法
这种方案与中间件的方案流程比较类似,不过改造成本相对较低,不需要增加任何中间件。
这时流程如下:
- 写请求发往主库,同时缓存记录操作的 key,缓存的失效时间设置为主从的延时
- 读请求首先判断缓存是否存在
- 若存在,代表刚发生过写操作,读请求操作主库
- 若不存在,代表近期没发生写操作,读请求操作从库
这种方案相对中间件的方案成本较低,但是呢我们此时又引入一个缓存组件,所有读写之间就又多了一步缓存操作。
总结
我们引入主从架构,数据读写分离,目的是为了解决业务快速发展,请求量变大,并发量变大,从而引发的数据库的读瓶颈。
不过当引入新一个架构解决问题时,势必会带来另外一个问题,数据库读写分离之后,主从延迟从而导致数据不一致的情况。,
为了解决主从延迟,数据不一致的情况,我们可以采用以下这几种方案:
- 忍受大法
- 数据库同步写方案
- 选择性强制读主
- 中间件选择路由法
- 缓存路由大法
上面的方案都有各自的优点,当然也有相应的缺点,我们需要根据自己的业务情况,选择相应的解决方案。
好了,今天的文章就到此。
我是楼下小黑哥,下周见~
参考链接
- 数据库主从不一致,怎么解?
欢迎关注我的公众号:小黑十一点半,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn
数据库读写分离这个坑,你应该踩过吧?相关推荐
- 学会数据库读写分离、分表分库
https://www.cnblogs.com/joylee/p/7513038.html 系统开发中,数据库是非常重要的一个点.除了程序的本身的优化,如:SQL语句优化.代码优化,数据库的处理本身优 ...
- Python开发之:Django基于Docker实现Mysql数据库读写分离、集群、主从同步详解 | 原力计划...
作者 | Pythonicc 责编 | 王晓曼 出品 | CSDN博客 简介 1.什么是数据库读写分离 读写分离,基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELET ...
- ceph集群和数据库到底是储存数据_Python开发之:Django基于Docker实现Mysql数据库读写分离、集群、主从同步详解 | 原力计划...
作者 | Pythonicc责编 | 王晓曼出品 | CSDN博客简介1.什么是数据库读写分离读写分离,基本的原理是让主数据库处理事务性增.改.删操作(INSERT.UPDATE.DELETE),而从 ...
- 跟我学Springboot开发后端管理系统5:数据库读写分离
在Matrix-web后台管理系统中,使用到了数据库的读写分离技术.采用的开源的Sharding-JDBC作为数据库读写分离的框架.Matrix-Web后台数据库这一块采用的技术栈如下: 使用Myba ...
- 解决数据库读写分离(转)
如何配置mysql数据库的主从? 单机配置mysql主从:http://my.oschina.net/god/blog/496 常见的解决数据库读写分离有两种方案 1.应用层 http://neore ...
- .net core发布 正在发现数据上下文_使用EF Core实现数据库读写分离
以下文章来源于朝夕Net社区 ,作者Eleven 朝夕Net社区 朝气.丰富.活跃的.Net社区,朝夕教育携百万粉丝共同打造!有技术,有感悟,有新闻,有照片,有故事,还有梦想! [精选转载]| 作者/ ...
- 简单好用!利用Spring AOP技术10分钟实现一个数据库读写分离方案
前言 最近我们的APP在线用户越来越多,接口的响应速度也是越来越慢,经过运维排查发现是由于并发查询太多导致的数据库压力比较大,架构师经过调研给出了数据库读写分离的解决方案,为了快速解决问题,我们最终采 ...
- PG SQL数据库读写分离的思路
PGSQL可以设置一台主多个SLAVE,这样我们需要一个办法能在程序里做到读写分离,查询了互联网,找到了下面的解决思路,请周枫在后继开发中测试下面的思路方案: 在应用层通过spring特性解决数据库读 ...
- java spring mysql配置_java相关:mysql+spring+mybatis实现数据库读写分离的代码配置
java相关:mysql+spring+mybatis实现数据库读写分离的代码配置 发布于 2020-4-4| 复制链接 分享一篇关于关于mysql+spring+mybatis实现数据库读写分离的代 ...
- 018:Django商城部署和数据库读写分离
本章知识点 1.Centos下python3环境的部署 2.Python uwsgi 3.Python uwsgi+nginx部署 4.mysql主从备份介绍 5.Linux下的mysql安装 6.基 ...
最新文章
- php中的for循环最多循环多少次,php - 我在PHP中有2次时间,如何运行foreach循环来遍历所有这些日子? (最后24小时同时循环1小时!) - 堆栈内存溢出...
- K-均值聚类算法对未标注数据分组(1)
- C++智能指针(二)模拟实现三种智能指针
- android保存字符到sd卡,android 保存TXT文件到SD卡方法
- luogu P1503 鬼子进村
- oracle11g调整表空间和临时表空间大小
- Linux 软件包管理
- 信捷plc485通信上位机_变频器、触摸屏和PLC建立MODBUS RTU通信的方法
- IDEA画Use Case(用例图)
- php 图形库 锯齿,PHP imageantialias - 是否使用抗锯齿(antialias)功能
- 使用mysql解决Excel换行统计问题
- 小白应该如何选择国内服务器?腾讯云、阿里云、小鸟云?
- 驾驶车辆 档位与油门的关系
- 两轮电动车被小米、哈啰们盯上了
- 图形之可见面判别算法
- element-plus popover的z-index问题
- static全局变量与普通的全局变量
- GEE入门【4】| 矢量数据FeatureCollection(行政区划分)
- Hastelloy C-276 (UNS N10276)含钨的镍-铬-钼合金
- 图片缩放情况下失真问题处理