国产数据库中读写分离实现机制

在数据库高可用架构下会存在1主多备的部署,备节点可以根据业务场景分发一部分流量以充分利用资源,并减轻主库的压力,因此在数据库的功能上需要读写分离来实现。

  • 充分利用备节点的资源,提升业务的吞吐量;
  • 防止运维等非业务查询访问主节点导致CPU飙升的场景,进而影响业务正常运行;
  • 在实现上考虑到主备时延过大的情况下,业务的容忍度以及规避措施;
  • 事务的处理上,更新操作在主节点而读在备节点,如何保证事务的一致性?

1、OceanBase数据库

OceanBase数据库的读写分离是由ODP(OceanBase Database Proxy)数据库代理实现的,读写分离就是数据在主节点修改后,数据同步到备节点,备节点提供数据的读取功能。OceanBase数据库支持读写分离到表的Partition级别,这也是原生的分布式数据库的优势。
在OceanBase数据库中使用读写分离功能,需要经过两部分设置:

  1. 设置SQL语句为弱读:所谓弱读是指读请求不要求读到最新的数据。对于正常的SQL为强读,使用弱读需要一定的设置。
  2. 修改路由策略:每个副本都可以提供弱读服务(主副本也可以提供读服务),通过设置路由策略,可以优先选择备副本。
1.1 弱读配置

1)在SQL语句中通过Hint

/*+READ_CONSISTENCY(WEAK)*/配置
select /*+READ_CONSISTENCY(WEAK)*/ * from t1;

2)通过OBProxy配置项obproxy_read_consistency设置

alter proxyconfig set obproxy_read_consistency = 1;

配置项默认值0表示强读、1表示弱读。SQL hint是语句级别的,配置项是实例级别的。通常可以配置多个OBProxy实例,选择其中部分OBProxy开启读配置作为运维查询使用。

1.2 修改路由策略

ODP通过配置项proxy_route_policy修改路由策略,通过设置为follower_first和follower_only可以让弱读请求优先发给备副本:

  • follower_first:优先选择备副本,如果备副本都不可用,选择主副本;
  • follower_only:选择备副本,如果备副本都不可用,断开和客户端的连接。

上述两个策略需要根据业务场景选择

2、PolarDB数据库

PolarDB MySQL版本的集群自带读写分离功能,在PolarDB控制台的数据库连接中配置读写模式,包括只读模式和可读可写模式(自动读写分离)。当配置可读可写模式后,写请求会自动转发到主节点,读请求会自动根据各节点的负载(当前未完成的请求数)转发到主节点或只读节点。

在这里插入图片描述

当客户端通过读写分离地址与数据库后端建立连接后,读写分离中间件会自动与主节点和各个只读节点建立连接。在同一个连接内(同一个session内),读写分离中间件会根据各个数据库节点的数据同步程度,来选择合适的节点,在保证数据正确的基础上(写操作之后的读有正确的结果),实现读写请求的负载均衡。另外,在配置可读可写模式后,可以配置主库是否接受读,当设置为否后,普通的读请求将不再发往主节点。而事务内,一致性要求的读请求仍会被发往主节点,以保证业务需求。另外,当所有只读节点出现故障后,读请求也会发往主节点。

在这里插入图片描述

同时读写分离模块自动对集群内的所有节点进行健康检查,当发现某个节点宕机或者延迟超过阈值(全局一致性读超时时间,取值范围:0~60000,默认为20ms)后,PolarDB将不再分配读请求给该节点,读写请求在剩余的健康节点间进行分配,以此确保单个只读节点发生故障时,不会影响应用的正常访问。当节点被修复后,PolarDB会自动将该节点纳回请求分配体系内。

2.1 请求转发逻辑

在配置了可读可写模式下,SQL语句的转发遵循以下规则:

  • 只发往主节点:DML和DDL操作、未开启事务拆分时所有事务请求、自定义函数、存储过程等;
  • 发往只读节点或主节点:非事务中的读请求、COM_STMT_EXECUTE命令等
  • 发往所有节点:所有系统变量的修改、COM_STMT_EXECUTE命令等

在这里插入图片描述

2.2 基于权重的动态负载均衡

PolarDB在可读可写(自动读写分离)模式下仅支持基于活跃请求数负载均衡的策略,优先选择最小活跃(并发)请求数的节点去路由请求,来保证多个只读节点间的负载均衡。该策略基本可以保证流量根据后端节点的负载均衡的路由到不同的后端节点上,即使后端节点的规格不一致,也能较好的进行负载均衡。

为了满足线上业务负载的多样性,PolarDB后续版本引入了基于权重的动态负载均衡策略,为每个节点配置不同的权重后,在后续的路由过程中,权重和并发请求数会同时作为参考标准去动态的调整最终的路由决策。在控制台“数据库代理服务配置”中,可以为不同的节点配置不同的权重,动态权重越大,节点的优先级越高,路由分发的流量也越大。

3、GaussDB数据库(for MySQL)

GaussDB(for MySQL)在创建数据库后,支持配置读写分离功能,通过代理地址将写请求自动访问主节点,读请求按照读权重配比或者活跃连接数情况分发到各个节点。在使用代理地址时,事务请求都会路由到实例的主节点,不保证非事务读的一致性,业务上有读一致性需求可以封装到事务中,可以使用事务拆分功能对事务中写之前的读请求进行拆分。

在GaussDB数据库控制台的数据库代理页面,新增数据库代理,包括代理模式、一致性级别、路由模式和添加的数据库节点。

  • 代理模式:支持读写模式和只读模式,只读模式下仅支持读请求业务,并且不支持DDL、DML操作和临时表操作;
  • 一致性级别:分为最终一致性和会话一致性,由于主备之间存在复制延迟,可能会导致每次读结果存在差异,默认情况只能保证最终一致性。当设置会话一致性后,保证在同一个会话内,保证每次读请求都是上一次更新操作后的结果。
  • 路由模式:包括权重负载和负载均衡,权重负载是根据设置的读权重比较分发请求,负载均衡是根据数据库节点的活跃连接数情况进行读请求分发。

在这里插入图片描述

另外,在SQL中通过hint方式指定发往主节点或只读节点。需要注意的是Hint注释仅作为路由建议,非只读SQL、事务中的场景不能强制路由到只读节点。

  • /FORCE_MASTER/强制路由到主节点;
  • /FORCE_SLAVE/强制路由到只读节点;
4、GoldenDB数据库

GoldenDB数据库的读写分离策略由连接实例级别的策略+语句级别的策略共同决定:

  • 在连接实例的服务端口进行配置,包括不开启读写分离、本地同城策略以及异地策略,其中本地同城策略需要配置权重;
  • SQL语句级别的读写分离策略,通过在SQL语句中增加hint实现,hint包括READMASTER、READBALANCE和READSLAVE

在这里插入图片描述

不同策略的组合如表所示:

在这里插入图片描述

另外,为避免只读数据节点读取的数据长时间和主数据节点不一致,当一个只读数据节点的延迟时间超过设置的延迟阈值,则不论该只读数据节点的读权重是多少,读请求都不会转发至该只读数据节点。在新版本的读写分离配置中,增加了“是否可读主”的配置,选择否后,当主备时延超过阈值时,依旧不会读主节点。

需要注意的是在多分片的实例中,当CN节点配置参数force_read_split后,开启读写分离时,显示的开启事务后,为了保证事务操作的一致性,事务内是不允许更新操作的,只允许只读事务,否则会提示“read-only transaction”的报错。

5、TDSQL数据库(for MySQL)

TDSQL数据库默认支持读写分离功能,包括3种读写分离的配置操作,其中非只读账号由参数gateway.mode.single_backend.rw_split控制:

  • 创建只读账号:创建账号时标记为只读,系统将根据只读策略将读请求发往备机,只读策略的配置分为以下场景:
    • 1:优先选择备机进行读操作,如果备机的延迟都大于设置的延迟,则从主机读取。
    • 2:只选择备机进行读操作,如果所有的备机都大于设置的延迟,则直接报错。
    • 3:只选择备机进行读取,忽略延迟参数,一般用于拉取binlog同步。
  • slave注释:参数rw_split=1,在SQL中增加slave模式的hint标签,将指定的SQL发往备机。比如在SQL中添加/slave/标记,该SQL会发送给备机。注意在mysql客户端使用时添加-c参数来解析注释,/slave/必须为小写,并且前后无空格。
//主机读//
select * from emp order by sal,deptno desc;
//从机读//
/*slave*/ select * from emp order by sal,deptno desc;
  • 全局自动读写分离:参数rw_split=2,配置该参数后会将读请求自动发送所有的备机。如果备机延迟较大,读到的数据可能会有延迟。如果是在事务中的读请求或者autocommit=0, begin/commit/rollback …等情况下,select也会发到主节点。

在这里插入图片描述

官方建议使用第1种和第2种方式配置读写分离,第三种配置方式会有一定程度的性能损耗。

6、OpenGauss数据库

openGauss数据库通过JDBC连接配置,支持一主多备情况下的读写分离,当URL中配置服务器地址时,可以通过URL中的属性标示targetServerType来区分JDBC返回的连接是否区分主机和备机。

  • Master:尝试连接到URL连接串中的主节点,如果找不到就抛出异常;
  • Slave:尝试连接到URL连接串中的备节点,如果找不到就抛出异常;
  • preferSlave:尝试连接到URL连接串中的备数据节点(如果有可用的话),否则连接到主数据节点;
  • any:尝试连接URL连接串中的任何一个数据节点,默认为“any”
##优先连接到备节点
jdbc:opengauss://node1,node2,node3/database?autoBalance=roundrobin&targetServerType=preferSlave
##只连接备机进行只读操作
jdbc:opengauss://node1,node2,node3/database?autoBalance=roundrobin&targetServerType=slave
##只连接主机进行读写操作
jdbc:opengauss://node1,node2,node3/database?autoBalance=false&targetServerType=master

OpenGauss数据库在一主多备的架构下,当主节点宕机的时候通过自主寻主的机制,找到主备切换后的新主。通过JDBC连接中的配置参数hostRecheckSecond,当主机状态发生更改时再次检查主机状态,快速地找到新的主机并恢复连接,默认值为10秒。JDBC连接流程如下:

在这里插入图片描述

  1. 设置参数开启targetServerType=xx。
  2. 判断输入的主机列表是否已经加入了候选主机列表中,如果不存在hostStatusMap中并且更新时间间隔超过10s(hostRecheckSecond)同时HostStatus状态也和现在一致,这时直接加入候选主机列表。
  3. 对列表进行遍历查找对应的节点信息,如果找到了,通过knownStatus查看状态是否一致,一致就执行sql查询语句,之后更新全局变量hostStatusMap及局部变量konwnStatus中存储。
  4. 当前操作的节点状态如果与用户选择的节点状态一致则返回主机连接,之后用户可以根据连接进行读或者写操作。
7、TiDB数据库

在TiDB数据库中读写分离通过follower read机制实现的,follower read是将TiKV读负载从Region的leader副本上offload到follower副本的负载均衡机制,以提升TiDB集群的吞吐能力并降低leader负载。TiDB的Follower Read功能开启通过变量tidb_replica_read配置:

set [session | global] tidb_replica_read = '<目标值>';
  • leader:默认值,将所有的读操作都发送给leader副本处理。
  • follower :选择Region的follower副本完成所有的数据读操作。
  • leader-and-follower:可以选择任意副本来执行读操作,此时读请求会在leader和follower之间负载均衡。
  • prefer-leader:优先选择leader副本执行读操作。当leader副本的处理速度明显变慢时,例如由于磁盘或网络性能抖动,TiDB将选择其他可用的follower副本来执行读操作。
  • closest-replicas:优先选择分布在同一可用区的副本执行读操作,对应的副本可以是leader或follower。如果同一可用区内没有副本分布,则会从leader执行读操作。
  • closest-adaptive:当一个读请求的预估返回结果大于或等于变量 tidb_adaptive_closest_read_threshold 的值时,TiDB会优先选择分布在同一可用区的副本执行读操作。
  • learner:选择learner副本执行读取操作。在读取时,如果当前Region没有learner副本,TiDB会报错。

Tidb数据库的Follower Read在实现机制上是follower节点使用Raft ReadIndex协议确保当前读请求可以读到当前leader上已经commit的最新数据。因此在TiDB层面,Follower Read只需根据负载均衡策略将某个Region的读取请求发送到follower节点。

8、总结

以上是几种国产数据库的读写分离实现,各个数据库厂商根据不同的业务场景在不同程度上实现了读写分离的功能,对比如下所示:

在这里插入图片描述

总体上而言,PolarDB、GaussDB和GoldenDB在读写分离的功能上更为完善,考虑到实例级别、SQL级别以及对事务的影响,还有在主备时延超过阈值时的处理。


参考资料:

  1. https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000819334
  2. https://help.aliyun.com/zh/polardb/polardb-for-mysql/user-guide/read-or-write-splitting-2
  3. https://support.huaweicloud.com/usermanual-gaussdbformysql/gaussdbformysql_11_0017.html
  4. https://www.goldendb.com/#/docsIndex/docs/instruction_TenantManagement
  5. https://cloud.tencent.com/privatecloud/document/123248530626207744/12325495623387545
  6. https://zhuanlan.zhihu.com/p/445545196
  7. https://docs.pingcap.com/zh/tidb/stable/follower-read#follower-read

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/30470.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《算法设计与分析》第五六章:回溯法与分支限界法

文章目录 回溯法分支限界法一、本章作业1.活动安排问题2.旅行商问题3.单源最短路径4.任务分配问题 二、算法积累1.回溯法求解01背包问题2.回溯法求解最大团问题3.回溯法求解n皇后问题4.回溯法求解地图着色5.回溯法求解哈密尔顿图6.回溯法求活动安排7.分支限界法求01背包问题8.分…

Flutter第十三弹 路由和导航

目标&#xff1a; 1.Flutter怎么创建路由&#xff1f; 2.怎么实现路由跳转&#xff1f;页面返回&#xff1f; 一、路由 1.1 什么是路由&#xff1f; 路由(Route)在移动开发中通常指页面&#xff08;Page&#xff09;&#xff0c;在Android中通常指一个Activity。所谓路由管…

小功率电机驱动方案中如何选择驱动IC

小功率电机驱动方案及驱动IC的选择 电机驱动作为工业4.0中工厂自动化整个闭环中的执行器环节&#xff0c;其性能好坏直接影响到整个闭环的性能。因此&#xff0c;工业4.0对电机驱动提出了更高的性能和功能要求&#xff0c;例如更快的响应速度、更高的带宽、更高精度的位置和速…

前端框架中的路由(Routing)和前端导航(Front-End Navigation)

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介前端框架中的路由&#xff08;Routing&#xff09;和前端导航&#xff08;Front-End Navigation&#xff09;1. 路由&#xff08;Routing&#xff09;1.1 定义1.2 路由的核心概念1.2.1 路由表&#xff08;Route Table&#xff09;1…

多模态大模型:基础架构

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则…

gitee添加别人的仓库后,在该仓库里添加文件夹/文件

一、在指定分支里添加文件夹&#xff08;如果库主没有创建分支&#xff0c;自己还要先创建分支&#xff09; eg:以在一个项目里添加视图文件为例&#xff0c;用Echarts分支在usr/views目录下添加Echarts文件夹&#xff0c;usr/views/Echarts目录下添加index.vue 1.切换为本地仓…

Linux系统安装Ruby语言

Ruby是一种面向对象的脚本语言&#xff0c;由日本的计算机科学家松本行弘设计并开发&#xff0c;Ruby的设计哲学强调程序员的幸福感&#xff0c;致力于简化编程的复杂性&#xff0c;并提供一种既强大又易于使用的工具。其语法简洁优雅&#xff0c;易于阅读和书写&#xff0c;使…

重学java 66.IO流 转换流

且敬我疯狂&#xff0c;生命中不败的篇章 —— 24.6.11 一、字符编码 计算机中储存的信息都是用二进制数表示的&#xff0c;而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。[按照某种规则&#xff0c;将字符存储到计算机中&#xff0c;称为编…

打造一个属于你的桌面天气 超级有个性的天气桌面

打造一个属于你的桌面天气 超级有个性的天气桌面。大家好&#xff0c;今天我们带来一个非常有趣的桌面天气工具&#xff0c;喜欢桌面diy的你&#xff0c;快点用上它吧&#xff01; 桌面上的美化&#xff0c;是许多爱美用户的心血和热爱。每个地方的美化&#xff0c;都是自己亲…

机器学习课程复习——隐马尔可夫

不考计算题 Q:概率图有几种结构? 条件独立性的公式? 顺序结构发散结构汇总结构Q:隐马尔可夫模型理解? 概念 集合:状态集合、观测集合 序列:状态序列、观测序列

linux环境编程基础学习

Shell编程&#xff1a; 相对的chmod -x xx.sh可以移除权限 想获取变量的值要掏点dollar&#xff08;&#xff04;&#xff09; 多位的话要加个花括号 运算&#xff1a;expr 运算时左右两边必须要加空格 *号多个含义必须加转义符 双引号可以加反单&#xff0c;但是发过来就不行 …

【android】安卓入门学习

文档介绍&#xff1a;http://8.136.122.222/book/primary/kotlin/kotlin-intro.html 文档补充说明&#xff1a;https://blog.csdn.net/qq_42059717/category_12047508.html 一、搭建环境及工具安装 见文档 二、工具界面及项目文件介绍 ├── app //工程主模块名称 │ …

【Linux安装Conda环境的详细教程】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

PTP简介及Linux phy ptp驱动实现

1、PTP简介 PTP(precision time protocol)精确时间协议&#xff0c;是一种时间同步的协议&#xff0c;对应 IEEE 1588 标准&#xff0c;是基于网络数据包的一种时间同步协议&#xff0c;1588v2的同步精度可以达到ns级&#xff0c;但1588协议对硬件有依赖。 2、PTP原理 时间同…

DFS(一)

问题一(指数级选择) 从1~n这n个整数中任意选取多个&#xff0c;输出所有可能的选择方案。 首先想一下&#xff0c;在现实世界中&#xff0c;我们要如何解决这个问题。 应该是一个一个枚举&#xff0c;即每个数都可以有两个选择(选/不选)。共有种结果。 想一下&#xff0c;如…

PHP转Go系列 | 字符串的使用姿势

大家好&#xff0c;我是码农先森。 输出 在 PHP 语言中的输出比较简单&#xff0c;直接使用 echo 就可以。此外&#xff0c;在 PHP 中还有一个格式化输出函数 sprintf 可以用占位符替换字符串。 <?phpecho 码农先森; echo sprintf(码农:%s, 先森);在 Go 语言中调用它的输…

创建进程的常用方式

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中有多个模块可以创建进程&#xff0c;比较常用的有os.fork()函数、multiprocessing模块和Pool进程池。由于os.fork()函数只适用于Unix/Linu…

0617,递归问题(详细——好好好一入递归深似海)

目录 第七章&#xff08;函数&#xff09;思维导图总结&#xff1a;递归三问 01&#xff0c;电影院问题 理解递归的执行过程 02&#xff0c;FIBNACCI数列 不是说具有递归结构的问题&#xff0c;就可以用递归求解——存在大量的重复计算 法一&#xff1a;自顶向下求解 BUG…

ruoyi框架第二天,自定义接口,在若依框架显示数据

书接上文&#xff0c;搭建好若依&#xff0c;并且创建自己想要的模块。 让ruoyi框架显示自己想要的模块。 今天&#xff0c;我们就要自定义接口&#xff0c;模仿ruoyi框架收发数据模式&#xff0c;来创建自己的模块。 我们创建好自己想要的接口&#xff0c;我这个是无参的查…

【Java】已解决java.util.EmptyStackException异常

文章目录 一、问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决java.util.EmptyStackException异常 一、问题背景 java.util.EmptyStackException是Java在使用java.util.Stack类时可能会遇到的一个异常。这个异常通常在尝试从空的栈中弹出&am…