读写分离,作为一种常用的数据库访问优化手段,得到广泛的应用。本文尝试从读写分离的技术实现、适用场景及典型产品等角度,阐述这一技术的整体现状。
1).何为读写分离
读写分离,从字面理解就是将对数据库的读操作与写操作分离的一种优化手段。其最早起源于互联网快速发展时期,面对海量用户访问问题,通过这一技术来解决数据库性能瓶颈问题。目前已经成为非常常见的一种数据库访问优化技术。
2).读写分离好处
1).常用方案
目前业界流行的读写分离方案,通常都是基于上述主从模式的数据库架构,通过引入数据访问代理层,来实现访问动作的读写分离。引入数据访问代理的好处是源程序不需要做任何改动就可以实现读写分离,坏处是由于多了一层中间件做中转代理,性能上会有所下降,数据访问代理也容易成为性能瓶颈,并且还存在一定维护成本。还有另一种方式,是将数据访问代理层前置到应用侧,通过SDK方式与应用集成在一起,可避免独立一层所带来的性能损耗和维护成本高的问题。但这种方式对开发语言有一定要求,存在适用性问题。
2).技术要点
读写分离功能的好与不好,主要是在易用性和灵活度问题。前者是关心如何让业务开发像操作单个主库一样,无需过多关注主从读写分离的细节,只需要做好相应读写配置后,就无需考虑写主读从的细节。后者是解决用户多变的业务场景和拓扑变化,并可实现自动适应。这其中是需要解决一系列技术问题,如下面这些常见的问题。
❖ 判断读写操作
如何判断读写操作,是读写分离面临的首要问题。判断方式可大致分为自动和手动两种,前者是通过显式的方式由用户来指定;后者则是自动进行判断,用户无需关心。这两种判断方式往往是互补的,可配合来使用。下面是常见判断逻辑及处理:
基于语法解析
当中间件获取到应用发送的SQL字符串时,对其进行完整的语法解析,可以最大程度的获取SQL字符串中的信息,例如类型、操作对象等。基于语法的判断,就能够自动针对不同语句类型进行读写分发,可以最大限度的减少应用的适配工作。
使用语法解析是相对来说较为友好的方式,无需开发人员感知即可实现读写操作分离。但这其中存在难点,就是如何准确判断出只读操作存在一定困难,例如使用函数、存储过程、触发器或诸如“SELECT ... FOR UPDATE”类的操作。此时,是需要引入辅助机制进行判断,可采取配置名单方式来辅助分析;或者通过Hint、API的方式强制指定走写库或读库。除此之外,还有些命令也需要规范是否可在备库执行,如COPY、SHOW、SET、BEGIN...END等。
❖ 如何处理事务
事务类操作,往往意味着数据变化,在读写分离中如何处理呢?通常有两种思路,一种是简单粗暴方式,将所有事务及关联操作全部发送到主机;一种是更为精确的处理,即分析事务内的语句序列,将事务中先写后读的对象进行关联,一起发送到主机,确保数据正确,而把和写操作无关的读操作,进行拆分,发送到备机执行。后一种处理方式能最大限度的利用读写分离,当然需要解决对象前后关系这一问题。
❖ 解决主备延迟
基于副本方式的延迟是常见的,也是读写分离在设计之初就需考虑的问题。其通常的处理思路可以有多种:
数据库优化
最后一种就是尽量避免出现延迟,常见对数据库有些可优化的措施。例如尽量减少在主节点上执行大事务操作、减少主库索引进而减小写入开销、主备库采用不同存储引擎提升效率等等。当然这些方案只能起到一定作用,无法完全避免延迟问题。
❖ 灵活负载策略
针对多个读库,读写分离组件还需提供灵活的负载均衡策略,常见的如随机、轮询、权重等等。这其中有几个特殊情况需要考虑:
位置感知
针对多AZ、多Region的情况,不同读库承载的角色不同,有的只作为备选主库不承担读、有的作为远程灾备等,因此在读写分离中希望能感知到这些信息,有所区别对待。往往可通过设置标签的方式解决,根据不同标签设置不同策略。
❖ 解决读一致性
在读写分离中,当存在多个读库下,会因为延迟不同,出现读取不一致的情况。即路由到不同的读库,读取的数据鲜活度不同。这对于前端应用会造成一定困扰,解决的方法可采用会话粘性的策略,针对同一会话路由到同一读库,避免出现读不一致。
❖ 拓扑结构感知
如果读写分离访问的数据集群拓扑发生变化,例如主备发生切换,写操作要到新的主库;亦或是增加了备库数量,流量可以打到新备库等,这些都是需要读写分离组件感知到底层数据库拓扑的变化。这里的难点在于几个方面:
人为干预能力
除因故障等原因发生的拓扑变化外,有时还需人工干预读写分离。如发生机器维护、数据库升级等情况下,可提前通过人工手段,从拓扑结构中摘除相关节点,做到更加平顺。
❖ 个性化诉求
除了上述要点外,还有些用户个性化的需求。如某个数据库用户的访问只走主库,某类应用的访问只走主库等,这类需求比较分散,比较好的处理方式是提供一定的脚本扩展能力,类似lua扩展Nginx的方式。
1).数据库优化手段对比
读写分离技术,是一种有效的数据库访问优化手段,但不是唯一。随着业务增长,达到一定规模后,提升数据库承载能力可以有多种方式,从大的分类来看可分为业务层优化、架构层优化、访问层优化与数据库优化几个方面。
数据库-水平扩展
对分库分表类似,但通常初始投入较大,对应用存在一定侵入性。
从上述对比可见,读写分离,可以说是对应用侵入最小,也最容易实现的优化手段。相对投入不到,就可取得一定效果。特别是对于大量读请求和少量写请求的业务场景,会有不错的效果。
2).读写分离适用场景
读写分离是一种简单有效的优化方式,但不是万能,其有着明显的适用场景特征。
业内有很多读写分离方案,一类是采用中间件思路开发,以开源产品为主;一类是数据库产品,内置读写分离功能。下面简单介绍下几种产品路线:
1).MySQL-Proxy
MySQL-Proxy是MySQL官方提供的MySQL中间件服务。MySQL-Proxy实际上是在客户端请求与MySQLServer之间建立了一个连接池。所有客户端请求都是发向MySQL-Proxy,然后经由MySQL-Proxy进行相应的分析,判断出是读操作还是写操作,分发至对应的MySQLServer上。对于多节点Slave集群,也可以起做到负载均衡的效果。
其中proxy-backend-addresses是master服务器,proxy-read-only-backend-addresses是slave服务器。
2).Apache ShardingSphere
Apache ShardingSphere 是一款开源的数据库中间件产品,并在Apache基金会毕业,可以说是非常成熟的开源项目。其产品内置了丰富的功能,包括读写分离能力,具体包括:
balance,读写分离策略
writeType,写模式
switchType,切换模式
3,基于MySQL galary cluster的切换机制
4).RDS数据库代理(以RDS PG为例)
数据库代理是数据库RDS提供的一款安全、稳定、高性能,对应用完全透明的数据库中间层服务。数据库代理是位于数据库服务端和应用服务端之间的网络代理服务,代理服务端代替应用服务端数据库发送和接受所有数据库请求,进而可以在代理服务层上实现比如读写分离、连接池、端对端加密、防闪断等附加功能。通过数据库代理用户只需要通过一个链接地址即可实现读写分离架构,读写属性和只读属性的多样化选择满足了不同业务场景。
数据库代理支持了PostgreSQL的协议,并且具备对用户的请求连接认证权限的能力。代理中的路由策略是核心:可以通过hint中指定固定的实例节点来转发流量;也能够将事务内写操作之前的读请求转发到只读实例,降低主实例负载;路由策略还可以根据用户自定义的只读模式或读写模式对请求进行不同的分发执行。健康巡检模块周期性感知读写分离架构的拓扑变化情况,在实例节点不健康或者超过延迟阈值,会自动把读请求路由到其他的只读实例上。
5).OceanBase
OceanBase 数据库天然支持读写分离的功能,即通过 OBProxy 代理服务和修改 OBServer 的配置即可实现业务的读写分离策略。OceanBase 数据库在读取数据时,提供了两种一致性级别:强一致性和弱一致性。
弱一致性是指请求优先路由给备副本,不要求读取最新数据。
通过应用侧为执行的 SQL 添加 SQL Hint 来显性开启弱一致性读就可以实现基于注释的读写分离功能,同时也衍生出如下三种常用的读写分离策略:
6).KunlunBase
KunlunBase是一个开源、高性能的分布式关系数据库,支持混合负载、PB级数据量管理并提供毫秒延迟的新一代数据库解决方案。
KunlunBase 的读写分离在计算层的远程查询优化器内实现的,当用户的SQL同时满足如下条件:
如果语句在显式事务中,则要满足:
远程查询优化器就会将相应的SQL 执行计划下发到从备机的节点上执行。 KunlunServer 会根据以下规则选择发送select语句到目标存储集群的哪个备机节点:
如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!
赞7
添加新评论2 条评论
2023-02-28 18:04
2023-02-20 14:39