PostgreSQL sharding : citus 系列5 - worker节点网络优化

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 标签PostgreSQL , citus , pgbouncer , 网络优化 , worker节点背景citus 节点间的网络需求:1、cn节点访问所有worker节点。oltp业务的访问较频繁。

标签

PostgreSQL , citus , pgbouncer , 网络优化 , worker节点


背景

citus 节点间的网络需求:

1、cn节点访问所有worker节点。oltp业务的访问较频繁。

2、重分布数据时,worker节点间相互访问。访问频度不大,OLAP业务常见,一旦有可能数据交换吞吐较大。

citus的cn节点连worker节点为有两种模式,

一种为事务级保持连接模式(每条SQL发起时建立连接,SQL结束后释放连接(除非在事务中,否则SQL结束立即释放连接)。),

另一种为会话级保持连接模式(会话发起时建立连接,会话结束后释放连接。)。

1、跑OLAP类的SQL时,使用的是第一种即时连接模式(OLAP场景并发不高,建立连接带来的额外开销不明显)

可以在worker节点打开参数进行跟踪

postgres=# show log_connections ;  
 log_connections   
-----------------  
 on  
(1 row)  
  
postgres=# show log_disconnections ;  
 log_disconnections   
--------------------  
 on  
(1 row)  

例子,

以下两条SQL均为即时短连接模式(Custom Scan (Citus Task-Tracker) Custom Scan (Citus Real-Time))。

postgres=# set citus.task_executor_type =task;  
ERROR:  invalid value for parameter "citus.task_executor_type": "task"  
HINT:  Available values: real-time, task-tracker.  
  
  
postgres=# set citus.task_executor_type ='task-tracker';  
SET  
postgres=# explain select count(*) from pgbench_accounts ;  
                                                     QUERY PLAN                                                        
---------------------------------------------------------------------------------------------------------------------  
 Aggregate  (cost=0.00..0.00 rows=0 width=0)  
   ->  Custom Scan (Citus Task-Tracker)  (cost=0.00..0.00 rows=0 width=0)  
         Task Count: 128  
         Tasks Shown: One of 128  
         ->  Task  
               Node: host=172.24.211.224 port=1921 dbname=postgres  
               ->  Aggregate  (cost=231.85..231.86 rows=1 width=8)  
                     ->  Seq Scan on pgbench_accounts_106812 pgbench_accounts  (cost=0.00..212.48 rows=7748 width=0)  
(8 rows)  
  
  
postgres=# set citus.task_executor_type ='real-time';  
  
postgres=# explain select count(*) from pgbench_accounts ;  
                                                     QUERY PLAN                                                        
---------------------------------------------------------------------------------------------------------------------  
 Aggregate  (cost=0.00..0.00 rows=0 width=0)  
   ->  Custom Scan (Citus Real-Time)  (cost=0.00..0.00 rows=0 width=0)  
         Task Count: 128  
         Tasks Shown: One of 128  
         ->  Task  
               Node: host=172.24.211.224 port=1921 dbname=postgres  
               ->  Aggregate  (cost=231.85..231.86 rows=1 width=8)  
                     ->  Seq Scan on pgbench_accounts_106812 pgbench_accounts  (cost=0.00..212.48 rows=7748 width=0)  
(8 rows)  

2、跑OLTP查询时(通常并发很高,前端有连接池(保持会话)),为会话级保持连接模式(Custom Scan (Citus Router))。

以下SQL为长连接模式(不会立即释放,而是等会再释放,以降低高并发时连接带来的开销)

postgres=# explain select * from pgbench_accounts where aid=5;  
                                                                QUERY PLAN                                                                  
------------------------------------------------------------------------------------------------------------------------------------------  
 Custom Scan (Citus Router)  (cost=0.00..0.00 rows=0 width=0)  
   Task Count: 1  
   Tasks Shown: All  
   ->  Task  
         Node: host=172.24.211.224 port=1921 dbname=postgres  
         ->  Index Scan using pgbench_accounts_pkey_106836 on pgbench_accounts_106836 pgbench_accounts  (cost=0.28..2.50 rows=1 width=97)  
               Index Cond: (aid = 5)  
(7 rows)  

看以上两种场景,CITUS应该说设计得已经很不错了。既能满足TP也能满足AP。

但是前面也说了,连接保持是在事务或会话层面的,如果查询量大,或者用户使用了短连接,建立连接的开销就会很凸显。

newdb=> \c postgres postgres  
You are now connected to database "postgres" as user "postgres".  
  
postgres=# select * from pgbench_accounts where aid=1;  
 aid | bid | abalance |                                        filler                                          
-----+-----+----------+--------------------------------------------------------------------------------------  
   1 |   1 |     7214 |                                                                                       
(1 row)  
  
Time: 11.264 ms  -- 包括新建连接的开销  
  
postgres=# select * from pgbench_accounts where aid=1;  
 aid | bid | abalance |                                        filler                                          
-----+-----+----------+--------------------------------------------------------------------------------------  
   1 |   1 |     7214 |                                                                                       
(1 row)  
  
Time: 0.905 ms  -- 已建立连接  

使用pgbouncer,解决建立连接的开销

pic

在worker节点上,部署pgbouncer,所有与worker节点建立的连接都通过pgbouncer连接池,以此来保持住连接,降低worker节点频繁新建连接的开销。

部署方法

1、所有worker节点

pgbouncer

yum install -y pgbouncer  

配置

vi /etc/pgbouncer/pgb.ini  
  
[databases]  
newdb = host=/tmp dbname=newdb port=1921 user=digoal pool_size=128 reserve_pool=10  
[pgbouncer]  
logfile = /var/log/pgbouncer/pgbouncer.log  
pidfile = /var/run/pgbouncer/pgbouncer.pid  
listen_addr = 0.0.0.0  
listen_port = 8001  
auth_type = any  
auth_file = /etc/pgbouncer/userlist.txt  
pool_mode = session  
server_reset_query = DISCARD ALL  
max_client_conn = 5000  
default_pool_size = 128  
; 最大不要超过4倍CPU数  

启动

pgbouncer -d -u pgbouncer /etc/pgbouncer/pgb.ini  

在一个citus集群中,可以同时存在直连worker或者通过pgbouncer连接worker。

不同的database可以有不同的配置。

以下例子,新建一个database,使用pgbouncer连接worker.

2、所有节点(包括cn, worker)

新建数据库,插件

su - postgres  
psql -c "create role digoal login;"  
psql -c "create database newdb;"  
psql -c "grant all on database newdb to digoal;"  
psql -U postgres newdb -c "create extension citus;"  

cn节点

将worker添加到集群配置,使用pgbouncer的连接端口

su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.224', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.230', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.231', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.225', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.227', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.232', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.226', 8001);\""      
su - postgres -c "psql -U postgres newdb -c \"SELECT * from master_add_node('xxx.xxx.xxx.229', 8001);\""     

MX配置,同样,将worker节点添加到元数据同步中。

psql newdb postgres  
select * from master_add_node('xxx.xxx.xxx.224',8001);    
select * from master_add_node('xxx.xxx.xxx.230',8001);    

开启同步到元数据。

select start_metadata_sync_to_node('xxx.xxx.xxx.224',8001);    
select start_metadata_sync_to_node('xxx.xxx.xxx.230',8001);    

测试

1、tpc-b 长连接测试

pgbench -i -s -U digoal newdb  
psql -U digoal newdb  
  
select create_distributed_table('pgbench_accounts','aid');  
select create_distributed_table('pgbench_branches','bid');    
select create_distributed_table('pgbench_tellers','tid');    
select create_distributed_table('pgbench_history','aid');  
pgbench -M prepared -v -r -P 1 -c 64 -j 64 -T 120 -U digoal newdb -S  

性能与不使用pgbouncer差不多,因为使用了长连接测试简单SQL(本身citus就使用了会话级连接保持,没有短连接问题)。

citus第一次QUERY的隐藏耗时

在一个新建的会话中,第一次查询总是需要需要耗费更多的时间(如果没有建立连接,那么包括建立连接的时间。即使已建立连接,也需要耗费额外的一些时间)(具体原因可以跟踪分析一下code)。

以下使用的是pgbouncer连接worker,因此第一次QUERY不包含建立连接的时间。

newdb=> \q  
postgres@digoal-citus-gpdb-test001-> psql newdb digoal  
psql (10.5)  
Type "help" for help.  
  
\timing  
  
newdb=> select * from pgbench_accounts where aid=5;  
 aid | bid | abalance |                                        filler                                          
-----+-----+----------+--------------------------------------------------------------------------------------  
   5 |   1 |        0 |                                                                                       
(1 row)  
  
Time: 6.016 ms  -- 不包括新建连接(已使用pgbouncer建立),但是也多了几毫秒  
-- 但是相比未使用pgbouncer,已经降低了5毫秒左右的延迟。
  
newdb=> select * from pgbench_accounts where aid=5;  
 aid | bid | abalance |                                        filler                                          
-----+-----+----------+--------------------------------------------------------------------------------------  
   5 |   1 |        0 |                                                                                       
(1 row)  
  
Time: 0.989 ms  

多出的几毫秒,我们社区的小伙伴邓彪、王健给出了原因如下,很多地方需要检查安装的citus版本与citus.control控制文件的版本是否兼容(比如加载分布式TABLE的RELCACHE时,第一次访问就是这个问题),不兼容报错:

详见代码

https://github.com/citusdata/citus/blob/3fa04d8f2c8f27b1377fe4c1468ee47358117e3c/src/backend/distributed/utils/metadata_cache.c

/*
 * CheckAvailableVersion compares CITUS_EXTENSIONVERSION and the currently
 * available version from the citus.control file. If they are not compatible,
 * this function logs an error with the specified elevel and returns false,
 * otherwise it returns true.
 */
bool
CheckAvailableVersion(int elevel)
{
	char *availableVersion = NULL;

	if (!EnableVersionChecks)
	{
		return true;
	}

	availableVersion = AvailableExtensionVersion();

	if (!MajorVersionsCompatible(availableVersion, CITUS_EXTENSIONVERSION))
	{
		ereport(elevel, (errmsg("loaded Citus library version differs from latest "
								"available extension version"),
						 errdetail("Loaded library requires %s, but the latest control "
								   "file specifies %s.", CITUS_MAJORVERSION,
								   availableVersion),
						 errhint("Restart the database to load the latest Citus "
								 "library.")));
		return false;
	}

	return true;
}
 

/*
 * AvailableExtensionVersion returns the Citus version from citus.control file. It also
 * saves the result, thus consecutive calls to CitusExtensionAvailableVersion will
 * not read the citus.control file again.
 */
static char *
AvailableExtensionVersion(void)
{
	ReturnSetInfo *extensionsResultSet = NULL;
	TupleTableSlot *tupleTableSlot = NULL;
	FunctionCallInfoData *fcinfo = NULL;
	FmgrInfo *flinfo = NULL;
	int argumentCount = 0;
	EState *estate = NULL;

	bool hasTuple = false;
	bool goForward = true;
	bool doCopy = false;
	char *availableExtensionVersion;

	InitializeCaches();

	estate = CreateExecutorState();
	extensionsResultSet = makeNode(ReturnSetInfo);
	extensionsResultSet->econtext = GetPerTupleExprContext(estate);
	extensionsResultSet->allowedModes = SFRM_Materialize;

	fcinfo = palloc0(sizeof(FunctionCallInfoData));
	flinfo = palloc0(sizeof(FmgrInfo));

	fmgr_info(F_PG_AVAILABLE_EXTENSIONS, flinfo);
	InitFunctionCallInfoData(*fcinfo, flinfo, argumentCount, InvalidOid, NULL,
							 (Node *) extensionsResultSet);

	/* pg_available_extensions returns result set containing all available extensions */
	(*pg_available_extensions)(fcinfo);

	tupleTableSlot = MakeSingleTupleTableSlot(extensionsResultSet->setDesc);
	hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward, doCopy,
									   tupleTableSlot);
	while (hasTuple)
	{
		Datum extensionNameDatum = 0;
		char *extensionName = NULL;
		bool isNull = false;

		extensionNameDatum = slot_getattr(tupleTableSlot, 1, &isNull);
		extensionName = NameStr(*DatumGetName(extensionNameDatum));
		if (strcmp(extensionName, "citus") == 0)
		{
			MemoryContext oldMemoryContext = NULL;
			Datum availableVersion = slot_getattr(tupleTableSlot, 2, &isNull);

			/* we will cache the result of citus version to prevent catalog access */
			oldMemoryContext = MemoryContextSwitchTo(CacheMemoryContext);

			availableExtensionVersion = text_to_cstring(DatumGetTextPP(availableVersion));

			MemoryContextSwitchTo(oldMemoryContext);

			ExecClearTuple(tupleTableSlot);
			ExecDropSingleTupleTableSlot(tupleTableSlot);

			return availableExtensionVersion;
		}

		ExecClearTuple(tupleTableSlot);
		hasTuple = tuplestore_gettupleslot(extensionsResultSet->setResult, goForward,
										   doCopy, tupleTableSlot);
	}

	ExecDropSingleTupleTableSlot(tupleTableSlot);

	ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					errmsg("citus extension is not found")));

	return NULL;
}

pgbouncer连接池连接worker的好处

1、对于业务层短连接会有比较好的效果。可以降低至少5毫秒左右的延迟。

2、对于大量复杂查询(需要motion的查询),可以减少节点间的连接数。

同时也需要注意pgbouncer瓶颈:带宽,QPS(单核,单个pgbouncer进程峰值QPS约5万)

参考

man 1 pgbouncer

man 5 pgbouncer

《PostgreSQL 连接池 pgbouncer 使用》

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
3月前
|
负载均衡 网络协议 网络性能优化
动态IP代理技术详解及网络性能优化
动态IP代理技术通过灵活更换IP地址,广泛应用于数据采集、网络安全测试等领域。本文详细解析其工作原理,涵盖HTTP、SOCKS代理及代理池的实现方法,并提供代码示例。同时探讨配置动态代理IP后如何通过智能调度、负载均衡、优化协议选择等方式提升网络性能,确保高效稳定的网络访问。
415 2
|
3天前
|
Kubernetes Shell Windows
【Azure K8S | AKS】在AKS的节点中抓取目标POD的网络包方法分享
在AKS中遇到复杂网络问题时,可通过以下步骤进入特定POD抓取网络包进行分析:1. 使用`kubectl get pods`确认Pod所在Node;2. 通过`kubectl node-shell`登录Node;3. 使用`crictl ps`找到Pod的Container ID;4. 获取PID并使用`nsenter`进入Pod的网络空间;5. 在`/var/tmp`目录下使用`tcpdump`抓包。完成后按Ctrl+C停止抓包。
30 12
|
9天前
|
机器学习/深度学习 数据采集 算法
基于PSO粒子群优化的CNN-LSTM-SAM网络时间序列回归预测算法matlab仿真
本项目展示了基于PSO优化的CNN-LSTM-SAM网络时间序列预测算法。使用Matlab2022a开发,完整代码含中文注释及操作视频。算法结合卷积层提取局部特征、LSTM处理长期依赖、自注意力机制捕捉全局特征,通过粒子群优化提升预测精度。适用于金融市场、气象预报等领域,提供高效准确的预测结果。
|
1月前
|
机器学习/深度学习 计算机视觉
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
68 10
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
|
23天前
|
机器学习/深度学习 数据采集 算法
基于WOA鲸鱼优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目基于MATLAB 2022a实现时间序列预测,采用CNN-GRU-SAM网络结构,结合鲸鱼优化算法(WOA)优化网络参数。核心代码含操作视频,运行效果无水印。算法通过卷积层提取局部特征,GRU层处理长期依赖,自注意力机制捕捉全局特征,全连接层整合输出。数据预处理后,使用WOA迭代优化,最终输出最优预测结果。
|
3月前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
233 80
|
15天前
|
机器学习/深度学习 数据采集 算法
基于GA遗传优化的CNN-LSTM-SAM网络时间序列回归预测算法matlab仿真
本项目使用MATLAB 2022a实现时间序列预测算法,完整程序无水印。核心代码包含详细中文注释和操作视频。算法基于CNN-LSTM-SAM网络,融合卷积层、LSTM层与自注意力机制,适用于金融市场、气象预报等领域。通过数据归一化、种群初始化、适应度计算及参数优化等步骤,有效处理非线性时间序列,输出精准预测结果。
|
1月前
|
传感器 算法 物联网
基于粒子群算法的网络最优节点部署优化matlab仿真
本项目基于粒子群优化(PSO)算法,实现WSN网络节点的最优部署,以最大化节点覆盖范围。使用MATLAB2022A进行开发与测试,展示了优化后的节点分布及其覆盖范围。核心代码通过定义目标函数和约束条件,利用PSO算法迭代搜索最佳节点位置,并绘制优化结果图。PSO算法灵感源于鸟群觅食行为,适用于连续和离散空间的优化问题,在通信网络、物联网等领域有广泛应用。该算法通过模拟粒子群体智慧,高效逼近最优解,提升网络性能。
|
1月前
|
机器学习/深度学习 数据采集 算法
基于GWO灰狼优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目基于MATLAB2022a,展示了时间序列预测算法的运行效果(无水印)。核心程序包含详细中文注释和操作视频。算法采用CNN-GRU-SAM网络,结合灰狼优化(GWO),通过卷积层提取局部特征、GRU处理长期依赖、自注意力机制捕捉全局特征,最终实现复杂非线性时间序列的高效预测。
|
2月前
|
机器学习/深度学习 数据采集 算法
基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目基于MATLAB2022a实现时间序列预测,采用CNN-GRU-SAM网络结构。卷积层提取局部特征,GRU层处理长期依赖,自注意力机制捕捉全局特征。完整代码含中文注释和操作视频,运行效果无水印展示。算法通过数据归一化、种群初始化、适应度计算、个体更新等步骤优化网络参数,最终输出预测结果。适用于金融市场、气象预报等领域。
基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版