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

本文简要讨论“读写分离”技术

时间:2023-03-20 02:05:52 科技观察

读写分离,作为一种常见的数据库访问优化方法,应用广泛。本文试图从读写分离技术实现、适用场景、典型产品等角度来阐述该技术的整体现状。1.读写分离:概述1).什么是读写分离?读写分离从字面上理解就是一种将数据库的读写操作分离的优化方法。它起源于互联网高速发展时期。面对海量用户访问的问题,该技术被用来解决数据库性能的瓶颈问题。它已经成为一种非常普遍的数据库访问优化技术。2).读写分离的好处提升访问性能通过引入读写分离技术,将之前集中在单点的访问压力分散到更多的节点上。可以用更多的资源来支撑业务系统,有效提升整体的访问性能。提高稳定性通过读写分离,可以有效规避异常操作带来的风险。一个常见的例子是大型查询语句,由于访问的数据规模巨大,占用了大量的CPU资源。通过分离承载,可以避免影响更重要的写操作。提高资源利用率为了更好地保护数据,数据库系统通常采用多副本技术对数据进行冗余保护,但如果备份副本不能提供业务访问,则会造成资源浪费,而读写分离可以有效利用只读副本,提高整体资源利用率。提高可用性通过引入更多的节点来承载读写操作,结合负载均衡和高可用检测技术,可以避免单点故障的可用性问题。提高访问效率通过使用不同的节点分别承载读写,也可以缓解锁带来的争用问题,提高单个节点的访问效率。更大的优化空间针对读操作的特殊性,通过分离独立的资源,可以采用独特的优化技术,进一步提高访问效率。2、读写分离:技术实现1).应用方案目前业界流行的读写分离方案,通常都是基于上述的主从数据库架构。通过引入数据访问代理层,实现访问动作的读写分离。引入数据访问代理的好处是源程序无需任何修改即可实现读写分离。缺点是由于增加了一层中间件作为中转代理,性能会下降,数据访问代理容易成为性能瓶颈。而且还有一定的维护成本。另一种方式是将数据访问代理层放在应用端之前,通过SDK的方式与应用集成,可以避免独立层带来的性能损失和维护成本高的问题。但是这种方法对开发语言有一定的要求,存在适用性问题。2).技术要点读写分离功能好不好,主要看易用性和灵活性。前者关心的是如何让业务开发像操作单个主库一样,没有过多关注主从读写分离的细节,只做相应的读写配置后,就不需要考虑编写主-读-从的细节。后者是解决用户不断变化的业务场景和拓扑变化,可以实现自动适配。其中是一系列需要解决的技术问题,例如以下常见问题。?判断读写操作如何判断读写操作是读写分离面临的首要问题。判断方法大致可分为自动和手动两种。前者由用户以显式方式指定;后者是自动判断的,用户无需关心。这两种判断方法往往是相辅相成的,可以结合使用。下面是常见的判断逻辑和处理:基于不同的端口连接,这种实现方式在读写分离功能上不是很好,因为这种方式和应用程序自己的实现没有太大区别,但是逻辑直接连接不同的数据库成为连接中间件软件服务器的不同端口并没有给应用系统开发带来实质性的简化。使用基于SQL匹配的正则表达式匹配是一种相对容易的解决方案。它不需要由应用程序修改。只需要在中间件中添加正则匹配规则,读写分发的逻辑就可以在中间件中完成。读写分离的效果取决于中间件正则匹配规则的编写质量。基于Hint应用系统发送SQL时,可以添加Hint来告诉中间件将SQL发送到哪里。中间件对特定规则的提示进行解析,从而将具有不同提示的语句分发到不同的数据库节点。基于语法分析中间件在获取应用程序发送的SQL字符串时,会进行完整的语法分析,最大程度获取SQL字符串中的信息,如类型、操作对象等。基于语法的判断,针对不同的句型自动进行读写分配,最大限度地减少应用程序的适配工作。使用语法分析是一种比较友好的方式,可以在开发者不知情的情况下实现读写操作的分离。但是这里面有一个难点,就是如何准确判断只读操作存在一定的困难,比如使用函数、存储过程、触发器,或者“SELECT...FORUPDATE”这样的操作。这时候就需要引入判断的辅助机制,可以使用配置列表的方式来辅助分析;或者可以使用Hint和API的方法强制写入或读取库。此外,还需要指定一些命令是否可以在备库执行,如COPY、SHOW、SET、BEGIN...END等。?如何应对事务性操作,往往意味着数据发生变化,如何应对读写分离?通常有两种思路,一种是简单粗暴的方式,将所有交易及相关操作发送给主机;另一种是更精确的处理,即分析事务中的语句顺序,将事务中先写后读的对象关联起来,一起发送给主机,保证数据正确,并将与写操作无关的读操作拆分,发送给备机执行。后一种处理方式可以最大限度的利用读写分离,当然要解决对象上下文的问题。?解决主从延迟基于复制方式的延迟很常见,也是读写分离设计之初需要考虑的问题。处理方法有很多种:强制从主库读写的方案是最简单粗暴的,也是实际工作中最常用的方案。通过判断主备节点的延迟,决定使用主库还是备库。通常,延迟判断可以封装在中间层,前端应用不需要感知。它只需要配置延迟阈值。当超过这个阈值时,它会自动去主库。如果下次访问时延迟低于阈值,则可以重新进入备库。当然,这种方式无疑会增加主库的压力。轮换并重试备用数据库。当备库读取不到最新的数据时,另一种思路是多读几次或者尝试读取其他备库。其核心是读取最新数据的判断,通常在应用开发时需要考虑。同时还要制定退化方案,什么情况下读主库会退化。结合缓存来解决比如延迟是常态,短时间内很难解决,引入缓存可以达到立竿见影的效果。其原理是数据写入主库时,同步或异步写入缓存,应用读取时先读取缓存,失败时才读取数据库。该方案由于引入了缓存组件,略显复杂,需要解决缓存和数据库同步更新失败的问题;同时对应用端有一定的影响,需要缓存感知。更好的处理方式是将它们封装在中间层,统一处理访问逻辑。最后一类数据库优化是尽可能避免延迟。对数据库有一些可优化的措施是很常见的。例如,尽量减少主节点上大事务操作的执行,减少主库的索引以减少写入开销,使用不同的存储引擎来提高效率等等。当然,这些解决方案只能起到一定的作用,并不能完全避免延迟问题。?灵活的负载策略对于多个读库,读写分离组件还需要提供灵活的负载均衡策略,如随机、轮询、权重等。有几种特殊情况需要考虑:当不同的QoS读库的服务能力不同时,它们所能提供的服务保障不同,需要提供例如对读写进行干预的权重配置分离。当然,更好的办法是提供服务质量评价机制,可以根据各个图书馆的服务能力进行分配。位置感知针对的是多AZ、多Region的情况。不同的阅读图书馆有不同的作用。有的只作为备份主库不承担读取,有的作为异地容灾使用。因此,我们希望能够在读写分离中感知到这些。信息的处理方式不同。往往可以通过设置标签来解决,根据不同的标签设置不同的策略。?解决读一致性在读写分离中,当有多个读库时,会因为延迟不同而出现读不一致的情况。即路由到不同的阅读库,阅读数据的新鲜度不同。这会给前端应用带来一些麻烦。解决方案可以是使用sessionstickiness策略,将同一个session路由到同一个阅读库,避免阅读不一致。?拓扑结构感知如果读写分离访问的数据集群拓扑发生变化,例如发生主从切换,写操作需要到新的主库;两者都需要读写分离组件感知底层数据库拓扑的变化。这里的难点在于几个方面:准确感知变化。当有网络等原因,底层变化时,可能检测不到读写分离组件;这时候会出现两种拓扑结构,一种是实际结构,另一种是读写分离组件感知到的结构。这个问题,一方面可以通过引入共识机制,增加多方判断来解决;另一方面,它也可以通过与高可用组件交互来减少误判。感知时效性问题当拓扑发生变化时,从变化到被读写分离组件感知需要时间。如果太短,会对数据库探索造成很大压力;如果太长,会影响整体恢复时间。有一个权衡。建议把这个能力开放给用户,用户可以根据自己的业务做决定。同时,还可以与高可用组件进行交互,第一时间将拓扑变化信息推送给读写分离组件,变被动检测为主动感知,提高时效性。人为干预能力除了故障等原因导致的拓扑变化外,有时还需要人为干预,将读写分离。在机器维护、数据库升级等情况下,可以提前通过人工的方式将相关节点从拓扑结构中移除,使其更加顺畅。?个性化诉求除了以上几点,用户还有一些个性化需求。比如某个数据库用户的访问只到主库,某类应用的访问只到主库。3.读写分离:最佳实践1)。与读写分离技术相比,数据库优化方法是一种有效的数据库访问优化方法,但并不是唯一的。随着业务的增长,达到一定的规模,提高数据库承载能力的方法有很多。从广义的分类来看,可以分为业务层优化、架构层优化、接入层优化、数据库优化。业务层——垂直拆分是最彻底的优化方式。拆分是在业务层完成的。投资很高,但结果往往相当可观。架构层——缓存/搜索通过引入缓存和搜索等技术,减轻数据库的压力,让数据库专注于有价值的操作。这种方式需要一定的转化工作量,收益取决于业务的数据需求。接入层——一种简单快速的读写分离优化方式,可以快速提升性能,对于部分场景效果明显。接入层-分库分表,分库分表的方式,原则上采用“大而小”的策略,但是对SQL兼容性要求高,会有一定的量的业务转型工作。预期的收益效果取决于数据的规模和业务需求。Database-verticalsplitting将现有数据库按业务拆分。难度和投资成本取决于之前的架构设计。难点在于拆分后的数据交互。预期收益不是很清楚。数据库垂直扩容是一种快速有效的数据库升级措施,对应用几乎没有影响,但需要一定的成本投入和升级所需的服务中断时间。收益获取存在上限瓶颈,预期中等。数据库-水平扩展类似于分库分表,但通常前期投入比较大,对应用程序有一定的侵入性。从上面的对比可以看出,读写分离可以说是对应用程序侵入性最小,最容易实现的优化方式。相对较少的投入可以取得一定的效果。特别是对于读请求量大,写请求量少的业务场景,会有很好的效果。2).读写分离适用场景。读写分离是一种简单有效的优化方法,但不是万能的。具有明显的适用场景特征。多读少写当单机数据库无法支撑业务的读写规模时,可以考虑读写分离。但是,需要考虑两者的比例。如果写操作的比例大于读操作的比例,就会对主库进行大量的写操作,读写分离达不到预期的减轻主库压力的效果。一般来说,两者的读写比越大,效果越好。当然,还要考虑写入规模不能也不可能高于单机数据库的支持规模。Readlimitedexpansion在超大规模读取的情况下也需要谨慎。读写分离可以实现读操作一定程度的水平扩展,但不是无限的。受限于数据库复制的效率和成本,扩容是有上限的。对于大规模应用,可以综合考虑缓存、数据拆分等多种方式。允许延迟主备模式不可避免地存在延迟,因此对延迟敏感的操作不适合采用该方案。对于不复杂的查询使用读写分离可以在一定程度上解决查询效率的问题,但是试图通过这种方式来解决复杂的查询并不是一个好主意。此类需求建议通过搜索引擎、OLAP等技术解决。4.读写分离:业界有很多针对典型产品的读写分离方案。一类是使用中间件思想开发的,主要是开源产品;另一类是内置读写分离功能的数据库产品。下面简单介绍主要产品:1).MySQL-ProxyMySQL-Proxy是MySQL官方提供的MySQL中间件服务。MySQL-Proxy实际上是在客户端请求和MySQLServer之间建立了一个连接池。所有的客户端请求都发送给MySQL-Proxy,然后MySQL-Proxy进行相应的分析,判断是读操作还是写操作,分发给对应的MySQLServer。对于多节点的Slave集群,也可以达到负载均衡的效果。#./mysql-proxy--daemon--log-level=debug--user=mysql--keepalive--log-file=/var/log/mysql-proxy.log--plugins="proxy"--proxy-backend-addresses="192.168.1.5:3306"--proxy-read-only-backend-addresses="192.168.1.6:3306"--proxy-lua-script="/root/soft/mysql-proxy/rw-splitting.lua"--plugins=admin--admin-username="admin"--admin-password="admin"--admin-lua-script="/root/soft/mysql-proxy/lib/mysql-proxy/lua/admin.lua"其中proxy-backend-addresses是主服务器,proxy-read-only-backend-addresses是从服务器。2).ApacheShardingSphereApacheShardingSphere是一个开源的数据库中间件产品,毕业于Apache基金会,可以说是一个非常成熟的开源项目。其产品内置了丰富的功能,包括读写分离能力,具体包括:支持动静态读写分离能力,支持自动拓扑感知和手动设置。支持多种数据库(如MySQL、PG、openGauss等)和多种架构(如MySQL主从、MGR等)支持多端访问(Driver、Proxy),可满足低延迟场景支持语法分析自动判断或Hint方式手动指定支持包括熔断等能力的人工干预方式,可适应各种场景支持类SQL管理配置方式,支持热加载配置,支持丰富的负载均衡算法,如图3下面)。MyCATMyCAT是一个开源的数据库中间件产品。读写分离功能是通过配置文件完成的,如下平衡,读写分离策略0,不开启读写分离机制,所有的读操作都发送到当前可用的writeHost1,所有的readHost和stand通过writeHost参与select语句的负载均衡2、所有Read操作随机分布在writeHost和readhost上3、所有读请求随机分布到readhostwriteType,写模式0,所有操作发送到第一个配置的writehost1,随机发送对所有配置的writehost2,不进行写操作switchType,切换模式-1,表示不自动切换1,默认值,表示自动切换2,根据mysql主从同步状态决定是否切换3,根据MySQLgalary集群的切换机制4).Aliyun-RDS数据库代理(以RDSPG为例)数据库代理是阿里云数据库RDS提供的安全、稳定、高性能、全透明的数据库中间层服务。数据库代理是一种位于数据库服务器和应用服务器之间的网络代理服务。代理服务器代替应用服务器数据库发送和接受所有数据库请求,然后可以在代理服务层实现读写分离、连接池、终端等。端到端加密和防闪等附加功能。通过数据库代理,用户只需要传递一个链接地址就可以实现读写分离架构,读写属性和只读属性的多样化选择满足不同的业务场景。数据库代理支持PostgreSQL协议,具有对用户请求连接进行鉴权的能力。agent中的路由策略是核心:通过在hint中指定一个固定的实例节点来转发流量;事务中写操作之前的读请求也可以转发给只读实例,减少主实例的负载;路由策略也可以根据用户自己定义的只读模式或读写模式对请求进行不同的执行。健康检查模块周期性地感知读写分离架构的拓扑变化。当实例节点不健康或超过延迟阈值时,它会自动将读取请求路由到其他只读实例。5).OceanBaseOceanBase数据库天然支持读写分离的功能,即通过OBProxy代理服务,修改OBServer的配置,实现业务的读写分离策略。OceanBase数据库在读取数据时提供了两种一致性级别:强一致性和弱一致性。强一致性是指请求路由到主副本读取最新的数据;弱一致性是指请求先路由到备份副本,不需要读取最新的数据。通过在应用端执行的SQL中加入SQLHint,显式启用弱一致性读,可以实现基于注解的读写分离功能,同时还衍生出以下三种常用的读写分离策略:6)。KunlunBaseKunlunBase是一个开源的,支持混合负载,管理PB级数据量,提供毫秒级延迟的新一代数据库解决方案的高性能分布式关系型数据库。KunlunBase的读写分离是在计算层的远程查询优化器中实现的。当用户的SQL同时满足以下条件时:当前SQL类型为select;SQL不包含用户自定义函数,除非当前事务是只读事务;if如果不在事务中(autocommit=on),则允许读写分离;如果语句在显式事务中,则必须满足:-如果在只读事务中,则允许读写分离;-如果是在读写事务中,那么该事务还没有更新数据;远程查询优化器将相应的SQL执行计划下发给从机节点执行。KunlunServer会根据以下规则选择哪个备节点向目标存储集群发送select语句:根据节点权重值(ro_weight)根据网络延迟(ping)根据master的数据一致性延迟进行选择-从副本(延迟)