当前位置: 首页 > 科技观察

如果你想把阅读和写作分开,我给你一些经验

时间:2023-03-22 12:44:21 科技观察

本文转载自微信公众号《猿世界》,作者尹继焕。转载本文请联系元天地公众号。读写分离是应用中提高数据访问性能最常用的技术。当用户数量增加,访问量增加时,单节点数据库不可避免地会遇到性能瓶颈。很多场景基本上是读多写少,自然要增加多个从节点来分担主节点的压力。应用访问读写分离后,难免会出现一些意想不到的问题。本文主要介绍一些经常遇到的问题。如有其他问题,欢迎留言补充。实现方法对于读写分离的使用,主要有两种方法,客户端方法和代理方法。客户端方法可以使用Spring自带的AbstractRoutingDataSource实现,也可以使用开源框架实现,比如Sharding-JDBC。代理方式需要写一个代理服务来管理所有的节点,应用不需要关注多个数据库节点的信息。你可以自己实现,使用开源框架,或者使用商业云服务。数据延迟说到数据延迟,首先要了解主从架构的原理。数据的增删改查操作在主库上执行,查询在从库上执行。当数据刚插入到主库,然后马上查询时,很有可能数据还没有同步到从库,就会出现查询不到的情况。比如我之前在一些网站上发表过文章。发表后,跳转到列表页,发现没有新发表的文章。我刷新了页面,又找到了。从这个角度看,这是读写分离后数据延迟造成的现象。是否解决强制路由数据延迟一般取决于业务场景。对于实时性要求不高的业务场景,允许一定的延迟。对于实时性要求高的场景,唯一的办法就是直接从主库查询,这样可以及时读取到最新插入或修改的数据。强制路由是一种解决方案,即将读请求强制分发到主库查询。大多数中间件支持提示语法/FORCE_MASTER/和/FORCE_SLAVE/。以Sharding-JDBC为例,框架提供了HintManager来强制路由。使用方法如下:HintManagerhintManager=HintManager.getInstance();hintManager.setMasterRouteOnly();为了使用方便,建议封装一个注解,添加到需要实时查询的业务方法中,在上面的注解中,通过切面设置强制路由。注解用法:@MasterRoute@OverridepublicUserBOgetUser(Longid){log.info("查询用户[{}]",id);if(id==null){thrownewBizException(ResponseCode.PARAM_ERROR_CODE,"id不能为空");}UserDOuserDO=userDao.getById(id);if(userDO==null){thrownewBizException(ResponseCode.NOT_FOUND_CODE);}returnuserBoConvert.convert(userDO);}方面设置:@AspectpublicclassMasterRouteAspect{@Around("@annotation(masterRoute)")publicObjectaroundGetConnection(finalProceedingJoinPointpjp,MasterRoutemasterRoute)throwsThrowable{HintManagerhintManager=HintManager.getInstance();hintManager.setMasterRouteOnly();try{returnpjp.proceed();}finally{hintManager.close();}}}事务中的事务读取请求,使用主库还是从库?对于这个问题,最简单的方法就是使用主库进行事务中的所有操作。在事务中,经常会出现插入然后重新查询的场景。这个时候事务没有提交,即使同步很快,从库也没有数据,只能去主库。但是还有一些请求只需要查询从库即可。对事务中的所有操作强制路由不是很好。Sharding-JDBC里面的实践很好。对于同一个线程,同一个数据库连接,如果有写操作,后续的读操作都会从主库读取,保证数据的一致性。如果我们在数据写入之前有查询请求,我们还是去从库,减轻主库的压力。动态强制路由决定了哪些接口在功能开发时强制走主库。这个时候我们会在代码上控制路由,也就是上面说的自定义注解。如果有的没有添加,但是在线运行的时候发现主库还是可以用的。这时候就需要修改代码,重新发布。动态强制路由可以结合配置中心实现,通过配置确定哪些接口需要强制路由,然后通过Filter中的HintManager进行设置,避免代码改动和重启。还可以通过方面将动态路由配置到业务方法级别。分流场景一:假设你有一个主节点和两个从节点,读请求比较多,两个从节点的压力有点大。这时只能增加第三个slave节点来分担压力。现象是主库压力不大,写入比较少。考虑到成本,可以不增加第三个从节点吗?场景二:假设你有一个8核64G的主库,一个8核64G的从节点数据库,4核32G的从库,从配置来看,4核32G的从库处理能力是肯定的低于其他两个。这时候如果不自定义流量分配比例,数据库压力会很低。过高引起的问题。当然这样也可以避免使用不同规则的从库。以上场景需要能够管理请求。Sharding-JDBC提供了读写分离的路由算法。我们可以自定义算法来管理流量分配。实现算法类:publicclassKittyMasterSlaveLoadBalanceAlgorithmimplementsMasterSlaveLoadBalanceAlgorithm{privateRoundRobinMasterSlaveLoadBalanceAlgorithmroundRobin=newRoundRobinMasterSlaveLoadBalanceAlgorithm();@OverridepublicStringgetDataSource(Stringname,StringmasterDataSourceName,ListslaveDataSourceNames){StringdataSource=roundRobin.getDataSource(name,masterDataSourceName,slaveDataSourceNames);//控制逻辑,比如不同的从节点(不同的配置)可以有不同的比例returndataSource;}@OverridepublicStringgetType(){return"KITTY_ROUND_ROBIN";}@OverridepublicPropertiesgetProperties(){returnroundRobin.getProperties();}@OverridepublicvoidsetProperties(Propertiesproperties){roundRobin.setProperties);}(基于属性SPI机制配置:org.apache.shardingsphere.core.strategy.masterslave.RoundRobinMasterSlaveLoadBalanceAlgorithmorg.apache.shardingsphere.core.strategy.masterslave.RandomMasterSlaveLoadBalanceAlgorithmcom.cxytiandi.kitty.db.shardingjdbc.algorithm.lavegoBalanceAlgorithmcom.cxytiandi.kitty.db.shardingjdbc.algorithm.lavegoBalanceAlgorithmRithm读写分离配置:spring.shardingsphere.masterslave.load-balance-algorithm-class-name=com.cxytiandi.kitty.db.shardingjdbc.algorithm.KittyMasterSlaveLoadBalanceAlgorithmsspring.shardingsphere.masterslave.load-balance-algorithm-type=KITTY_ROUND_ROBIN作者简介:尹继焕,单纯技术爱好者,?作者,《Spring Cloud 微服务 入门 实战与进阶》,公众号Apeland创始人