当前位置: 首页 > 后端技术 > Node.js

NodeEE双写和分布式事务要点12

时间:2023-04-03 17:08:27 Node.js

数据库和缓存双写问题计算机领域的任何问题都可以通过增加一个抽象的“层”来解决。为了减少业务中对热点数据不必要的db查询,往往会加一层缓存来解决I/O性能问题。然而,多一层I/O意味着多一层更新维护和容错保证。在修改db中的一些数据时,经常会面临缓存更新的问题。这里简单介绍一下数据库和缓存双写的问题以及双写策略在业务场景中的使用方法。缓存更新时机缓存有以下几种情况需要更新:没有缓存,回源到db后加上缓存超时。重复上一步修改数据库,更新缓存。如果没有缓存或者缓存超时:查询db设置缓存如果缓存存在,需要更新db,有多种缓存更新策略:先更新db,再更新缓存,先删除缓存,然后更新db,先更新db,删除缓存。本节主要讨论更新db时如何更新缓存。并暂时忽略缓存操作失败(如网络原因、redis服务不可用等)。如果业务场景中不出现修改同数据字段竞争的问题,那么三种更新策略毫无疑问都可以使用。如果存在缓存竞争情况,那么先排除先策略:](https://si.geilicdn.com/viewm...如上图,如果A和B先后修改db,会出现最终缓存和db不一致的现象,会导致在缓存超时或者下一次更新之前的时间段内出现使用脏数据的现象.而业务方需要考虑缓存是否需要每次更新db都会立即刷新,如果在“写入频率远低于写入频率时是否需要频繁刷新缓存?”第二种策略,先删除缓存再更新数据库,旨在以牺牲性能为代价,尽可能的减少脏缓存的使用,但是这种情况下还是有可能存在脏缓存的:](https://si.geilicdn.com/viewm...如上图如图,A先删除缓存,同时开始更新db;同时B查询缓存为空,然后Querydb,因为db的读性能高于写,数据库隔离级别默认为commitread,所以B查询db的数据往往是旧数据.之后B查询并更新缓存,导致缓存超时或者下次修改db的范围是脏数据。如果db底层是读写分离的,就更容易出现这种现象。B查询db读取数据库,A修改主库后需要一定时间同步,以保证从库中的数据是最新的。所以在这种情况下,缓存一定还是脏的。为了避免这种情况,A可以在更新db后的一定时间间隔后(通常是查询db的时间+设置缓存的时间)删除缓存,尽量缩短dirtycache的周期,返回新的请求源数据库并设置新的缓存数据。如下所示。](https://si.geilicdn.com/viewm...第三种策略是先更新数据库,再删除缓存。这种策略比较安全,几乎不会有脏缓存。即使有,会出现极度不合理的脏缓存情况:](https://si.geilicdn.com/viewm...如上图,缓存中有脏数据的前提是第二步取比第三步和第四步长,也就是读的时间比写的时间长,这种情况几乎不可能发生,即使发生了也可以通过延迟再次删除缓存来解决A(二删).上一节提到的缓存操作问题所有的缓存更新策略都是暂时忽略缓存在操作失败的前提下讨论的(比如网络原因,redis服务不可用等),如果缓存操作失败,必须通过业务代码重试、消息队列或者设置缓存超时来解决。业务代码重试,设置合理的重试次数和间隔时间。如果超时后缓存仍然无法运行,则需要等待缓存超时或人为干预;缓存操作失败后,消息队列会投递相应的消息,在非业务代码中重试;缓存超时是一种自下而上的解决方案,也就是允许缓存不一致的最长时间。遗憾的是对于分布式事务,在节点领域并没有JTA规范及其类似JAVA的实现。JTA规范中最核心的“transactionmanagerTM”,多由容器实现,如Commonjboss、websphere;TM接收业务层的事务请求,同时配合参与事务的dbms、mq等各种资源管理器RM,实现分布式事务的提交和回滚;同时,它还提供了在不同自治系统中的分布式事务。分布式事务的中心化解决方案有以下几种:1.两阶段提交2.三阶段提交3.异步保证4.TCC在JAVA等生态中已经证明了两阶段提交的低效率和不可抗拒性问题高并发和单点存在;三阶段提交虽然解决了两阶段单点,减少了协调者阻塞等待参与者的问题,但是仍然存在数据不一致的问题。因此,这两种理论模型实际上是不一致的。不符合实际业务场景。工程领域需要追求的是优化。可见,理论与现实之间还有很大的差距。那么在节点场景下,处理分布式事务的工程方法只有两种。解决方案。node中使用的异步保证模型可以使用相对简单的基于消息队列(也基于本地数据库表)的异步保证模型。将分布式长事务划分为多个本地事务,通过保证本地事务的可靠性实现分布式长事务的最终提交。如果分布式事务中涉及的本地事务失败回滚,则通过消息队列实现业务主动方的补偿,以达到最终的数据一致性。如下图所示:与异步保证相比,TCC模型相对重。有必要开发一个TCCTM来协调各种服务参与者。同时对参与事务的各个从服务的侵入性比较强。必须提供try、confirm、cancel三个接口。.其中try接口预留相关资源并保证数据一致性,confirm接口和cancel接口保证幂等性,执行或回滚try阶段预留的资源。其中,在业务中,主动调用所有参与分布式事务的从服务的try接口,并向TM上报执行状态,TM根据try阶段的结果完成后续执行或回滚操作,并记录分布式事务的状态传递和各个从服务的执行阶段等信息,便于跟踪。因此,在使用node实现分布式事务时,无需自研TCC中间件,即可根据业务特点扩展异步保底方案。