谈谈PolarDB-X在读写分离场景的实践

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 针对写少读多的业务可以考虑通过添加数据库节点来使其达到提升性能的目的,但添加节点,往往涉及到数据的搬迁,扩容周期比较长,很难应对徒增的业务流量,这个时候可以考虑采用读写分离的方式,将读写流量做分流,减轻主实例的压力,同时利用只读库横向的扩展能力,快速提升读性能。

在数据库使用过程中经常会遇到一些场景:

  1. 业务写流量一直相对比较稳定,但随着时间,数据不断增加,数据库的压力也会越来越大,写操作会影响到读请求的性能,做任何优化可能都达不到最终的效果;
  2. 在应用的用户访问量比较低的时候,一个数据库的读写能力是完全能够胜任的。但是在用户访问量增大的时候,数据库很快会成为瓶颈;

针对这种写少读多的业务可以考虑通过添加数据库节点来使其达到提升性能的目的,但添加节点,往往涉及到数据的搬迁,扩容周期比较长,很难应对徒增的业务流量。这个时候可以考虑采用读写分离的方式,将读写流量做分流,减轻主实例的压力,同时利用只读库横向的扩展能力,快速提升读性能。

其基本原理是让主数据库处理事务性查询,而从数据库处理select查询。当业务量非常大时,一台服务器的性能无法满足需求,就可以读写分离方式分摊负载,避免因负载太高而造成无法及时响应请求。目前业界实现读写分离的方案主要有两种:

基于程序代码内部实现
在代码中根据select,insert进行路由分类,这类方法也是目前生产环境应用最广泛的,优点是性能好,因为在程序代码中已经将读写的数据源拆分至两个,所以不需要额外的MySQL proxy解析SQL报文,在进行路由至不同数据库节点。缺点是通常该架构较复杂,运维成本相对较高。

基于中间Proxy(odp/mycat等)实现

Proxy一般位于客户端和服务器之间,代理服务器接到客户端请求后通过解析SQL文本再将SQL路由至可用的数据库节点中。优点是程序不需要改造可以实现无缝迁移,可移植性较好。缺点是性能相对前者略微逊色一些,并且并不是所有的读操作都能够被路由至从节点中。

上述方案都不能算是透明的,要不需要对业务代码进行改造,要不需要业务系统依然第三方组件;除此之外,业界主流的读写方案都无法做到一致性读,应用在使用弱一致性读时,要充分考虑主备副本的数据同步延时,并根据具体业务场景考虑延时的业务影响(脏读)是否能够接受。读写分离业务都还需要做额外的改造,以应对只读库异常或者延迟过大的时候下,对业务做降级处理。

为此,PolarDB-X内核侧提出一种提供了透明的强一致的读写分离能力,支持多种读写分离策略满足各类业务需求。

PolarDB-X Native 读写分离

PolarDB-X配置了多种读写策略,提供了透明的强一致的读写分离能力。简单的说其特点有:

  • 无论什么状况都不用担心误写了“备副本或只读副本”,因为它不支持写,写操作会被路由到主副本;
  • 无论什么时候不用担心“备副本或只读副本”故障,因为它会自动路由给其他正常的副本或者切回主副本;
  • 无论什么场景不用担心 “备副本或只读副本”读不到最新的数据,因为它提供的是强一致的读写能力;
  • 大查询不用担心打爆“主副本”,因为它支持将大查询路由给”备副本或只读副本“,避免对主副本造成压力。

其整体的方案设计如下:

PolarDB-X存储节点基于X-Paxos复制协议,整合了binary log,实现了统一的consensus log。consensus log不仅扮演了binary log的角色,同时还维护了LogIndex(全局一致性日志位点)。LogIndex中记录了主库了最新修改consensus log位点信息,当只读库做一致性复制的时候,也会不断更新自身的LogIndex信息。通过LogIndex我们可以确保在只读库上读到最新的数据,结合TSO可以确保读到已提交的一致性数据。路由到只读节点的强一致性读查询过程如下:

  1. 客户端把请求发送到CN;
  2. CN识别到请求会发送给只读实例,首先会从主实例DN节点获取当前最大LogIndex;
  3. CN把LogIndex +TSO  请求一起发送给只读节点;
  4. 只读节点根据接收到的LogIndex判断是否等到只读节点事务状态回放到相应位点;根据TSO判断数据可见性,给CN返回结果。

同时为了降低每次一次只读查询都会分别与主库交互获取LogIndex,我们为此做了异步Grouping Fetch Logindex优化,在高并发下尽可能将多次Fetch Logindex整合成一次请求,大大降低对主库的压力。

读写分离的操作管理

业务上不需要做任何改造,在需要的时候购买只读实例,默认情况下主实例就具备了读写分离的能力。如果要开启,需要在阿里控制台点击主实例上的集群地址-》配置管理。

会进入下只读分离的配置管理页面

操作

解释

资源配置

选择购买的只读实例加入到读写分离集群,只有在读写分离集群的实例才可以分担主实例的流量

智能读写分离

基于统计信息做代价估算,将偏AP的复杂查询路由给只读实例做MPP加速。非混合负载场景,可以选择关闭

只读流量占比

将只读流量设置按一定的比例路由给只读实例

数据一致性

强一致性: 路由到只读实例的请求可以读到已提交最新鲜的数据,但如果只读实例延迟大的话,路由到只读实例

弱一致性: 路由到只读实例的请求只是读到实例上可见的最新数据。

只读实例延迟阀值

只读实例延迟超过阈值,那么流量会打到其他延迟正常的只读实例上或者切回主实例。

只读可用性

当只读实例延迟超过阈值或者只读实例 HA时,则认为当前只读实例处于不可用的状态,流量自动切回主实例或者其他正常的只读实例上去。

用户真正在使用的时候,业务上完全不需要改造任何代码,一般只需要调整只读流量占比,调整为0意味着流量全部路由给主库;调整成>0,只读流量将按照设置的比例自动分流给只读实例。在这个基础上结合只读实例延迟阀值只读可用性,可以做到在只读实例异常状态下,流量自动切回主实例或者其他正常的只读实例上去。除了数据库级别的配置,PolarDB-X也支持session级别和query级别的读写配置,可灵活控制某个session下只读查询或者某个具体query的路由分发。

读写分离的可用性测试

为了衡量PolarDB-X 读写分离能力的可用性,我们利用sysbench 从正确性 性能角度设计了三种实验场景来验证。

测试环境

  • 测试所用实例规格

主实例规格2*16C128G (CN)+2*4C32G(DN), 2个只读实例规格  2*4C32G(CN) +  2*4C 32G(DN)

  • 测试所用压力机规格

ecs.g7ne.8xlarge(24 vCPU,48 GB内存)

测试参数配置

  • 修改参数ENABLE_COROUTINE的值为true,XPROTO_MAX_DN_CONCURRENT的值为4000,详细操作步骤请参见参数设置。
  • 通过命令行连接到PolarDB-X实例,在同一会话内执行如下SQL语句,关闭日志记录与CPU采样统计:

set ENABLE_SET_GLOBAL = true;

set global RECORD_SQL = false;

set global MPP_METRIC_LEVEL = 0;

set global ENABLE_CPU_PROFILE = false;

测试数据准备

nohup sysbench --config-file='sysb.conf' --create-table-options='partition by key(id)'  --tables='16' --threads='16' --table-size='10000000' oltp_point_select prepare &


实验一:一致性读的正确性

从附件中下载这次测试的oltp_insert_read_verify.lua脚本,该脚本逻辑比较简单,就是在主实例插入一条数据,测试在不同并发下在一致性读开启和关闭条件下,是否可以正确读到。一次实验的执行流程如下:


#准备表

sysbench --config-file='sysb.conf' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --tables='8' --threads={并发度} --time=60 oltp_insert_read_verify prepare


#运行

sysbench --config-file='sysb.conf' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --tables='8' --threads={并发度} --time=60 oltp_insert_read_verify run


#清理

sysbench --config-file='sysb.conf' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --tables='8' --threads={并发度} --time=60 oltp_insert_read_verify cleanup


测试结果

并发

不一致性次数(弱一致性读)

不一致性次数(强一致性读)

4

2

0

8

5

0

16

16

0

32

33

0

64

57

0

从测试结果看,强一致性读下,可以保证路由到只读实例的流量读到最新的数据;而非强一致性读下,无法保证。


实验二:高并发下吞吐表现

我们分别利用oltp_point_select和oltp_read_only两个脚本,测试了下在当前规格下高并发下的性能数据。


  • 点查场景

sysbench --config-file='sysb.conf' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --tables='16' --table-size='10000000' --threads=512  oltp_point_select run

测试了在不同只读实例个数,不同配置下的性能数据,其QPS表现如下

只读实例查询占比

主实例+一个只读实例(强一致性)

主实例+一个只读实例(弱一致性)

主实例+两个只读实例(强一致性)

主实例+两个只读实例(弱一致性)

0%

88201.17

88201.17

88201.17

88201.17

50%

124269.63

171935.56

171783.84

208648.34

100%

62891.86

89213.13

117133.83

169352.07


  • oltp_read场景

sysbench --config-file='sysb.conf' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --tables='16' --table-size='10000000' --range-size=5 --threads=512 oltp_read_only run

测试了在不同只读实例个数,不同配置下的性能数据,其QPS表现如下

只读实例查询占比

主实例+一个只读实例(强一致)

主实例+一个只读实例(弱一致)

主实例+两个只读实例(强一致)

主实例+两个只读实例(弱一致)

0%

29145.43

29145.43

29145.43

29145.43

50%

44084.40

55399.80

61698.85

73161.11

100%

23115.23

29235.73

42160.54

56393.54


从测试结果看:

1. 在强一致性读下,在TP读场景下流量从主实例切换到只读实例上吞吐的性能衰减20~30%,但是通过添加只读实例个数,性能可以做到一定的线性增加;

2.在弱一致性读下,在TP读场景下流量从主实例切换到只读实例上吞吐的性能未衰减,且通过添加只读实例的个数,性能可以做到线性增加;


实验三:低并发下RT表现

同样的我们分别利用oltp_point_select和oltp_read_only两个脚本,测试了下在(一个主实例+只读实例)下在强一致性读条件下,不同并发的RT表现。


  • 点查场景(ms)

并发

只读查询比例0%

只读查询比例100%

只读查询比例50%

4

0.61

0.89

0.87

8

0.62

0.91

0.89

16

0.64

0.94

0.90

32

0.71

1.04

0.99

64

1.18

1.27

1.27


  • oltp_read场景(ms)

并发

只读查询比例0%

只读查询比例100%

只读查询比例50%

4

11.65

21.89

17.63

8

12.21

22.69

17.29

16

14.21

23.95

18.23

32

20

27.17

21.50

64

40.5

50.7

31.37


从测试结果看:

  1. 低并发场景下,只读实例和主实例压力都不大,流量从主实例切换到只读实例上,RT衰减近30%~40%, 但都是在业务可以接受的RT范围内抖动;
  2. 随着并发增加,流量从主实例切换到只读实例上RT衰减会变得不明显,主实例的资源会成为影响RT的主要因素。


本文的读写分离路由的是非事务下的只读流量,这里给大家提一个问题:事务下的读查询是否可以做读写分离?

📎oltp_insert_read_verify.lua

相关实践学习
Polardb-x 弹性伸缩实验
本实验主要介绍如何对PolarDB-X进行手动收缩扩容,了解PolarDB-X 中各个节点的含义,以及如何对不同配置的PolarDB-x 进行压测。
相关文章
|
7月前
|
关系型数据库 物联网 PostgreSQL
沉浸式学习PostgreSQL|PolarDB 11: 物联网(IoT)、监控系统、应用日志、用户行为记录等场景 - 时序数据高吞吐存取分析
物联网场景, 通常有大量的传感器(例如水质监控、气象监测、新能源汽车上的大量传感器)不断探测最新数据并上报到数据库. 监控系统, 通常也会有采集程序不断的读取被监控指标(例如CPU、网络数据包转发、磁盘的IOPS和BW占用情况、内存的使用率等等), 同时将监控数据上报到数据库. 应用日志、用户行为日志, 也就有同样的特征, 不断产生并上报到数据库. 以上数据具有时序特征, 对数据库的关键能力要求如下: 数据高速写入 高速按时间区间读取和分析, 目的是发现异常, 分析规律. 尽量节省存储空间
608 1
|
7月前
|
关系型数据库 分布式数据库 对象存储
沉浸式学习PostgreSQL|PolarDB 5: 零售连锁、工厂等数字化率较低场景的数据分析
零售连锁, 制作业的工厂等场景中, 普遍数字化率较低, 通常存在这些问题: 数据离线, 例如每天盘点时上传, 未实现实时汇总到数据库中. 数据格式多, 例如excel, csv, txt, 甚至纸质手抄. 让我们一起来思考一下, 如何使用较少的投入实现数据汇总分析?
208 0
|
6月前
|
关系型数据库 定位技术 分布式数据库
沉浸式学习PostgreSQL|PolarDB 18: 通过GIS轨迹相似伴随|时态分析|轨迹驻点识别等技术对拐卖、诱骗场景进行侦查
本文主要教大家怎么用好数据库, 而不是怎么运维管理数据库、怎么开发数据库内核.
1069 1
|
2月前
|
关系型数据库 中间件 数据库连接
drds读写分离与只读实例
drds读写分离与只读实例
30 3
|
8月前
|
关系型数据库 分布式数据库 数据库
沉浸式学习PostgreSQL|PolarDB 4: 跨境电商场景, 快速判断商标|品牌侵权
很多业务场景中需要判断商标侵权, 避免纠纷. 例如 电商的商品文字描述、图片描述中可能有侵权内容. 特别是跨境电商, 在一些国家侵权查处非常严厉. 注册公司名、产品名时可能侵权. 在写文章时, 文章的文字内容、视频内容、图片内容中的描述可能侵权. 例如postgresql是个商标, 如果你使用posthellogresql、postgresqlabc也可能算侵权. 以跨境电商为力, 为了避免侵权, 在发布内容时需要商品描述中出现的品牌名、产品名等是否与已有的商标库有相似. 对于跨境电商场景, 由于店铺和用户众多, 商品的修改、发布是比较高频的操作, 所以需要实现高性能的字符串相似匹配功能.
143 0
|
3月前
|
关系型数据库 MySQL 分布式数据库
PolarDB MySQL企业版产品系列:满足不同场景需求的解决方案
PolarDB MySQL企业版产品系列:满足不同场景需求的解决方案 在数字化时代,企业对于数据处理的需求越来越多样化,对于数据库的选择也更为谨慎。PolarDB MySQL版为了满足不同场景的需求,提供了单节点、集群版、高压缩引擎(X-Engine)和多主集群(库表)4种不同的产品系列。下面我们将对这4种产品系列进行简要介绍,以帮助您更好地了解它们的特点和适用场景。
120 1
|
8月前
|
SQL 存储 druid
PolarDB-X 针对跑批场景的思考和实践
金融行业和运营商系统,业务除了在线联机查询外,同时有离线跑批处理,跑批场景比较注重吞吐量,同时基于数据库场景有一定的使用惯性,比如直连MySQL分库分表的存储节点做本地化跑批、以及基于Oracle/DB2等数据库做ETL的数据清洗跑批等。
|
4月前
|
关系型数据库 MySQL 分布式数据库
PolarDB auto_inc场景下的性能优化实践
PolarDB auto_inc场景下的性能优化实践 在数据库的使用场景中,并发插入数据或并发导入数据场景是最常见的。针对这一场景,PolarDB MySQL版进行了深度性能优化,以提高插入性能。本文将详细介绍PolarDB在auto_inc场景下的性能优化相关内容。
63 2
|
7月前
|
关系型数据库 分布式数据库 PolarDB
|
7月前
|
存储 SQL 数据库
典型场景 | PolarDB-X 如何支撑SaaS多租户
很多平台类应用或系统(如电商CRM平台、仓库订单平台等等),它们的服务模型是围绕用户维度(这里的用户维度可以是一个卖家或品牌,可以是一个仓库,等等)展开的。因此,这类型的平台业务,为了支持业务系统的水平扩展性,业务的数据库通常是按用户维度进行水平切分。

相关产品

  • 云原生数据库 PolarDB