一、问题&分析在读多写少的互联网业务场景中,往往“读”性能会成为第一瓶颈。随着业务的发展,数据库负载越来越高,逐渐成为系统的瓶颈。面对“读取”的性能瓶颈,解决问题的思路大致有以下几种:改进DB配置以获得更高的性能。使用更多的NX机器,升级DB的CPU、内存、磁盘等;使用更多的数据库来分担读取压力。对DB进行“拆分”,一个DB实例负责数据写入,一组DB实例负责数据查询,也就是常说的读写分离;读取压力转移到其他存储引擎。比如引入一个读性能更高的Cache,让Cache站在DB的前面,减少落在DB上的请求量;以上三种方案各有千秋,但性价比最高的还是“读写分离方案”:方案一、升级硬件资源简单粗暴,主要是出于资金方面的考虑;另外,硬件是有天花板的,钱不能解决所有问题;解决方案3,缓存是提高读性能的一大杀手,在追求性能的同时需要做很多事情。比如调整逻辑代码,保证一致性,增加运维成本等,同时也给系统引入了更多的复杂性。落地效果非常好。可有时候,牛刀杀鸡,怎么可能呢?方案2,适度介于两者之间,不需要过多的资金投入,也不需要过早引入太多的复杂性。2.读写分离是根据数据库部署架构来分离数据操作。Request,从节点主要处理读请求。通过引入多个副本来分发读请求,实现读请求的水平扩展。主副本和从副本之间的数据一致性是通过“复制”来实现的。读写分离架构有几个很重要的概念:主副本。也称为主节点,它可以接受读写请求;奴隶复制品。也称为从节点,它只能处理读请求;复制。基于“复制”技术的主从副本数据同步;路由。根据路由规则将请求分发到整个集群(主副本+从副本)。以最常见的MySQL主从架构为例:读请求的水平扩展可以通过扩展Slave来实现。核心流程如下:应用将写请求路由到Master节点;Master节点完成写入(transactioncommit)后,将变更写入Binlog;Slave节点从Master节点获取Binlog,并执行变更(涉及中继日志和并发复制),与Master数据保持一致;应用将读取请求路由到Slave节点,从Slave获取数据;备注:Slave太多会增加Master的压力,可以通过多级复制或者分区来解决。读写分离架构可以方便的扩展读请求,看起来很美好,但是需要解决两个问题:数据同步。如何保证主从副本之间的数据一致性,通常由存储引擎的“复制”机制来保证;请求路由。什么时候操作主副本,什么时候操作从副本,多副本之间如何做负载均衡?1、复制模式复制模式主要解决数据同步问题。通常比想象的要复杂。这里只介绍单主复制。对于多主复制,讨论起来太复杂了。(1)同步复制同步复制架构如图所示:核心流程:主节点处理完请求后,将复制信息同步到所有节点;所有节点返回后,将最终结果返回给用户;特点:优势强一致性保证,应用写入成功后,从节点和主节点之间达成共识,不存在影响写入性能的数据延迟问题。写入性能=Master+Max(Slave)影响可用性。从机异常直接影响写入,导致写入过程中断。常见应用场景:在实际开发中,很少使用这种方案,尤其是在CAP最终一致性思想的影响下,TiDB等NewSQL内部通过一致性协议保证强一致性。基于NRW理论保证可用性。这个很复杂,不在讨论范围内(2)异步复制。节点获取信息;特点优点:写入性能好缺点:存在数据丢失风险数据安全要求不高,性能要求高的应用场景,比如日志记录等“饮鸩止渴”的极端情况,比如秒杀,大促场景(3)半同步复制半同步复制是同步复制和异步复制的结合。架构如下:核心进程master节点处理完请求后,在部分节点上进行同步复制,等待复制完成,将最终处理返回给应用程序,结果其他剩余节点异步复制平衡性能和一致性。应用场景满足大部分业务场景。2.路由模式路由模式主要决定了请求如何分发到众多的数据库节点。(1)应用路由在应用层使用代码分发请求。整体架构如下:核心流程:为每个数据库节点构建一个DataSource,结合ORM框架,构建不同的DAO应用代码,根据业务场景调用不同的DAO实现,完成读写操作可以封装多个从节点DataSource成一个聚合DAO,内嵌负载均衡算法,不同从库之间路由特点:优点:简单,用编码实现,控制力最强缺点:对系统的存在性极强,需要修改大量逻辑代码场景:存在于需要极度控制的老项目或场景(2)SmartDataSources将多个DataSources封装成一个具有路由功能的SmartDataSource。整体架构如下:核心设计如下:每个数据节点对应一个DataSource,多个DataSource封装成一个SmartDataSourceSmartDataSource自动解析SQL,根据SQL类型自动完成请求路由。该应用程序仅与SmartDataSource通信。直接替换底层DataSource缺点:在集群的情况下,需要一个配置中心统一协调场景:特别是在高性能场景下使用(3)Proxy路由将SmartDataSource的核心功能抽取出来作为一个单独的服务。整体架构如下:核心设计:将SmartDataSource的功能抽取出来单独服务。它向下管理多个数据库节点。它向上暴露了标准的jdbc接口供应用程序使用。特点:优点。易于管理,所有管理动作都对Proxy层关闭;缺点。增加一层网络开销会对性能产生一定的影响;场景:用于管理场景通常在配置中心的基础上综合使用智能路由和代理路由两种模式:智能路由。对于应用,追求极致的性能;代理路由。用于数据库管理,追求管理的便捷性;配置中心。为智能路由和代理路由提供统一的配置信息。3.延迟挑战应用集成读写分离后,主要的挑战是:复制延迟。因此,在系统设计中,需要针对特定??场景进行特殊处理。1、更新场景,强制主切对于更新场景,为了避免主从延迟造成的写覆盖问题,通常采用强制主切策略。写覆盖的根本原因如下图所示:由于主从延迟,加载的聚合根不一定是最新的数据。因此,后续的修改和保存都是对过期的数据进行的,造成写丢失。备注:在乐观锁的保护下,不会有写丢失;面对这种情况,最简单的策略就是:强行砍master。具体过程如下:直接从Mater加载,防止Slave查询过期数据。SmartDataSource和Proxy都提供了强制mastercut的设置方法,这里就不过多介绍了。2.基于版本的智能路由如果下游能拿到最新版本,就可以根据版本智能获取数据。以领域事件场景为例,问题描述如下:核心流程如下:业务完成后,变更更新到DB,master更新完成后直接返回处理结果;从机开始异步同步,但完成时间不可控;业务向主题发送领域事件;下游业务监听消息后,向Slave查询数据。如果Slave没有同步过,就会出现拿不到或者拿不到过期数据的问题。对于这种场景,可以引入版本进行数据校验。基于Version的流程如下:核心流程如下:业务完成后,将业务变更和版本变更更新到DB。Master更新完成后,直接返回处理结果;Slave开始异步同步,但完成时间不可控;domainevent包含当前最新版本,发送给Topic;下游业务监听消息后,先从Slave获取数据,比较两个版本。如果大于等于msg中的版本,则直接使用;否则从Master加载,然后执行业务逻辑。注意:步骤4中的版本管理要封装在服务接口中,提供统一的版本参数接口;时间戳是一个特殊的版本,可以使用数据表的update_time作为版本。3、读自己的文读自己的文,简单地说:保存数据后,理解并阅读数据。由于复制延迟的存在,通常无法立即读取刚刚写入的数据。问题流程如下:核心流程:业务直接基于Master完成操作返回,异步复制变化到从节点UI跳转到下一页。页面会读取最新的数据(详情页、列表页)。由于主从复制延迟,可能获取不到最新数据(1)主动延迟最简单的解决方法是数据更新操作完成后,UI主动休眠几分钟。秒,然后继续下一步。整体流程如下:在跳转到新页面之前,添加一个加载页面,主动等待主从完成同步。当系统压力较高时,仍然无法从根源上解决问题。由于简单,在项目中也被广泛使用(2)UI主动等待动态添加会对用户造成一定的伤害,可以采用动态添加的方案来提升用户体验。核心点包括:处理更新请求后,直接返回最新数据,包括新增数据或修改数据;前端获取到数据后,直接对UI进行操作,比如追加到Table上,或者直接渲染详情页;(3)智能总开关UI的主动添加,只是蒙眼而已。用户刷新页面后可能看不到最新的数据。你可以试试强行切大师。xxx”强制切换master,其他请求默认为Slave时间间隔,请求中携带时间戳或版本,对请求进行切换master判断。4.总结简单回顾一下。写分离是一个提高系统读性能的重要手段,在实现读写分离时,需要解决复制和路由的技术问题,由于复制延迟的存在,需要对特殊的业务场景进行管理。
