今天共享以前进公司现在公司第一次发布项目时遇到的问题,数据库读写分离的漏洞。
前言中有这样的事情。 刚进公司的时候,就受到了这样的业务需求。
如果每个支付通道的支付失败,将返回特定的错误代码。 业务中需要将通道特定的错误代码转义为内部错误代码。 这样,对外就可以统一返回他们的错误代码。
这个需求其实并不难。 当时设计的系统体系结构如下。
添加规则的过程可以简单地分为三个步骤。
业务代表之所以在管理后台添加、修改映射规则数据库并删除缓存,是因为每次付款时都需要使用此场景。 通过使用缓存,可以避免每次都去数据库读取,提高读取速度。
后续的申请业务流程如下:
如果缓存中不存在映射规则,则会查询数据库并将其加载到缓存中。 如果缓存中已存在映射规则,则直接使用缓存中的映射规则。
这个业务流程其实比较简单,当时在测试环境下测试也没问题,但是后来发布在线环境遇到了奇怪的问题。
“添加规则有一段时间没有启用映射规则。 查看日志,在查询数据库时,没有数据。 " "
这是不可思议的。 已成功将新添加到日志中,但查询中没有数据。 但是,过了一会儿,再次调查后又有了数据。
查了一下代码,没有任何问题,第二天上班的时候问了同事,知道了问题的原因:
在线数据库采用主从体系结构,数据读写分离,数据查询来自库。 所有数据写入都直接操作主库,然后同步到从库。
“由于数据库同步延迟,在数据同步期间,主从数据不匹配,无法从库中查询最新数据。 " "
如果您的上一个数据库系统体系结构是单库或主备用结构,那么失眠仙人掌第一次迁移到数据读写分离体系结构时,这个漏洞也是很有概率的。
数据库系统体系结构的发展
首先了解数据库系统体系结构,最后看看如何解决主从同步延迟导致的数据不一致性。
在主体系结构业务发展的前一阶段,由于数据访问量较少,因此可以直接采用单库体系结构。
但是,我们通常不使用的基础架构,因为有单点问题。 如果数据库发生故障,在此期间业务将无法使用。 我们除了等待重启以外没有任何解决办法。
因此,添加备用库以实时同步主库中的数据。
如果“主库”出现故障,请手动使“主机”脱机,并将“备用”更改为“主机”以继续服务。
该体系结构部署维护简单,业务开发也不需要任何改造。
但是缺点也很明显,备用库只有在主库有问题时才被启用,可能会浪费一定的资源。
主从结构随着业务的发展,需求量大,数据量大,业务更加复杂,很快数据就会成为瓶颈。
由于大多数业务读写较少,数据库读取最容易成为系统瓶颈。
这个时候,我们可以提高可读性。 此时,我们可以采用的方案是增加实例、主从同步和数据读写之间的分离。
可见,该体系结构与主控系统没有太大区别。 主要区别在于在主从架构下,从库和主库一样,需要经常工作,主库提供写服务,库中只提供读服务。
如果后续阅读压力太大,还可以增加库的数量,水平扩展阅读能力。
主从体系结构可以解决读取瓶颈,但由于主从之间需要数据同步,因此这种自然会有一定的延迟。
在该延迟时间内,从库的读取中只能读取一个旧数据,这也是上述情况问题的真正原因。
接下来,让我们来看看如何优化这种情况。
忍受主从延迟解决方案大法的首要解决方案很简单,别无他法。 不管他,不读也没关系。 此时,业务不需要任何改造。 你好,我很好。 她也很好~
如果对数据一致性的业务要求较低,可以采用此方案。
数据同步写入方式主从数据同步方式通常以异步方式同步到备用库。
可以将其更改为同步方案,以便在主和从同步完成后恢复对主库的写入。
商业系统开始写入。 数据写入主库的写入请求必须等待返回数据读取从库,直到主从同步完成。 主从同步完成后,可以读取最新的数据。 我们只需要修改数据库之间的同步配置,业务层不需要修改,比较简单。
“但是,主库的写入需要等待主从完成,这增加了写入请求的延迟,降低了吞吐量。 " "
这在目前的在线业务中可能无法接受。
选择性强制引导主对需要强一致性的字段
景,我们可以将其的读请求都操作主库,这样「读写都在主库」,就没有不一致的情况。
这种方案业务层需要改造一下,将其强制性读主,相对改造难度较低。
不过这种方案相对于浪费了另一个数据库,增加主库的压力。
中间件选择路由法这种方案需要使用一个中间件,所有数据库操作都先发到中间件,由中间件再分发到相应的数据库。
这时流程如下:
写请求,中间件将会发到主库,同时记录一下此时写请求的 key(操作表加主键等)读请求,如果此时 key 存在,将会路由到主库一定时间后(经验值),中间件认为主从同步完成,删除这个 key,后续读将会读从库这种方案,可以保持数据读写的一致。
但是系统架构增加了一个中间件,整体复杂度变高,业务开发也变得复杂,学习成本也比较高。
缓存路由大法这种方案与中间件的方案流程比较类似,不过改造成本相对较低,不需要增加任何中间件。
这时流程如下:
写请求发往主库,同时缓存记录操作的 key,缓存的失效时间设置为主从的延时读请求首先判断缓存是否存在 若存在,代表刚发生过写操作,读请求操作主库若不存在,代表近期没发生写操作,读请求操作从库这种方案相对中间件的方案成本较低,但是呢我们此时又引入一个缓存组件,所有读写之间就又多了一步缓存操作。
总结我们引入主从架构,数据读写分离,目的是为了解决业务快速发展,请求量变大,并发量变大,从而引发的数据库的读瓶颈。
不过当引入新一个架构解决问题时,势必会带来另外一个问题,数据库读写分离之后,主从延迟从而导致数据不一致的情况。,
为了解决主从延迟,数据不一致的情况,我们可以采用以下这几种方案:
忍受大法数据库同步写方案选择性强制读主中间件选择路由法缓存路由大法上面的方案都有各自的优点,当然也有相应的缺点,我们需要根据自己的业务情况,选择相应的解决方案。
原作者:楼下小黑哥原文链接: 数据库读写分离这个坑,让刚入职的我一脸懵逼!
原出处: 程序通事
侵删