PostgresPro buildin pool(内置连接池)版本 原理与测试

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , 内置连接池 , pgbouncer , postgrespro


背景

PostgreSQL 与Oracle dedicate server 一样采用了线程模式,在连接数非常多时,性能下降会比较严重。

通常社区用户的做法是使用连接池,比如pgbouncer,但是使用PGbouncer也会引入一些使用上的不便利,比如transaction模式不能使用绑定变量等。在以下文章中做过一些较为详细的分析。

《阿里云 RDS PostgreSQL 高并发特性 vs 社区版本 (1.6万并发: 3倍吞吐,240倍响应速度)》

Postgrespro是俄罗斯的一家PG生态公司,

《透过postgrespro看PostgreSQL的附加功能》

内置连接池在他们的TODO列表有看到,最近放出了一版devel版本。

postgres buildin pool 版本安装

1、下载源码,切换分支

git clone https://github.com/postgrespro/postgresql.builtin_pool  
cd postgresql.builtin_pool  
git checkout conn_pool  
git branch conn_pool  

2、编译安装

./configure --prefix=/home/digoal/pgsql11_pool  
make -j 128  
make install  

3、修改环境变量

vi env_pg11_pool.sh   
  
export PS1="$USER@`/bin/hostname -s`-> "    
export PGPORT=4001  
export PGDATA=/data01/pg/pg_root$PGPORT    
export LANG=en_US.utf8    
export PGHOME=/home/digoal/pgsql11_pool  
export LD_LIBRARY_PATH=$PGHOME/lib:/lib64:/usr/lib64:/usr/local/lib64:/lib:/usr/lib:/usr/local/lib:$LD_LIBRARY_PATH    
export DATE=`date +"%Y%m%d%H%M"`    
export PATH=$PGHOME/bin:$PATH:.    
export MANPATH=$PGHOME/share/man:$MANPATH    
export PGHOST=$PGDATA    
export PGUSER=postgres    
export PGDATABASE=postgres    
alias rm='rm -i'    
alias ll='ls -lh'    
unalias vi   

4、设置环境变量

. ./env_pg11_pool.sh   

5、初始化数据库

initdb -D $PGDATA -U postgres -E UTF8 --locale=en_US.utf8 -X /data02/pg/pg_wal_4001  

6、配置数据库参数

cd $PGDATA  
vi postgresql.conf  
  
listen_addresses = '0.0.0.0'  
port = 4001  
max_connections = 20000  
superuser_reserved_connections = 13  
unix_socket_directories = '/tmp,.'  
tcp_keepalives_idle = 60  
tcp_keepalives_interval = 10  
tcp_keepalives_count = 10  
shared_buffers = 32GB  
maintenance_work_mem = 1GB  
dynamic_shared_memory_type = posix  
vacuum_cost_delay = 0  
bgwriter_delay = 10ms  
bgwriter_lru_maxpages = 500  
bgwriter_lru_multiplier = 5.0  
effective_io_concurrency = 0  
wal_level = minimal   
synchronous_commit = off  
full_page_writes = off  
wal_buffers = 128MB  
wal_writer_delay = 10ms  
checkpoint_timeout = 25min  
max_wal_size = 64GB  
min_wal_size = 16GB  
checkpoint_completion_target = 0.1  
max_wal_senders = 0  
random_page_cost = 1.1  
log_destination = 'csvlog'  
logging_collector = on  
log_truncate_on_rotation = on  
log_checkpoints = on  
log_error_verbosity = verbose  
log_timezone = 'PRC'  
datestyle = 'iso, mdy'  
timezone = 'PRC'  
lc_messages = 'C'  
lc_monetary = 'C'  
lc_numeric = 'C'  
lc_time = 'C'  
default_text_search_config = 'pg_catalog.english'  

内置连接池参数如下

# pool 配置  
session_pool_size=56   # 最好与CPU核数一致  ,如果有很多pool ports,可以考虑设小一点。    
session_pool_ports=0   # 如果配置为0,表示shared server与dedicate server共用一个端口, port = 4001    
                       # 如果配置为1,表示port = 4001为deadcate server port,port+1 为shared server ports.     
		       # 如果配置为大于1,port+1, port+2, .... 为shared server ports.     
		       # 如果要对应用透明,建议配置为0, 但是最佳实践建议配置为大于1,比如每对user/dbname 一个port。    
		       # postgres数据库不受pool限制,一律使用dedicate server.   

架构

pic

pic

7、启动数据库

pg_ctl start  

连接池参数介绍

1、pool包含两个参数

# pool 配置  
session_pool_size=56   # 最好与CPU核数一致  ,如果有很多pool ports,可以考虑设小一点。    
session_pool_ports=0   # 如果配置为0,表示shared server与dedicate server共用一个端口, port = 4001    
                       # 如果配置为1,表示port = 4001为deadcate server port,port+1 为shared server ports.     
		       # 如果配置为大于1,port+1, port+2, .... 为shared server ports.     
		       # 如果要对应用透明,建议配置为0, 但是最佳实践建议配置为大于1,比如每对user/dbname 一个port。    
		       # postgres数据库不受pool限制,一律使用dedicate server.   

在guc.c里面可以看到这两个参数的介绍

{  
  {"session_pool_size", PGC_POSTMASTER, CONN_AUTH_SETTINGS,  
          gettext_noop("Sets number of backends serving client sessions."),  
          gettext_noop("If non-zero then session pooling will be used: "  
                       "client connections will be redirected to one of the backends and maximal number of backends is determined by this parameter."  
                       "Launched backend are never terminated even in case of no active sessions.")  
  },  
  &SessionPoolSize,  
  10, 0, INT_MAX,  
  NULL, NULL, NULL  
},  
  
  
{  
  {"session_pool_ports", PGC_POSTMASTER, CONN_AUTH_SETTINGS,  
   gettext_noop("Number of session ports = number of session pools."),  
   gettext_noop("Number of extra ports which PostgreSQL will listen to accept client session. Each such port has separate session pool."  
                "It is intended that each port corresponds to some particular database/user combination, so that all backends in this session "  
                "pool will handle connection accessing this database. If session_pool_port is non zero then postmaster will always spawn dedicated (non-pooling) "  
                " backends at the main Postgres port. If session_pool_port is zero and session_pool_size is not zero, then sessions (pooled connection) will be also "  
                "accepted at main port. Session pool ports are allocated sequentially: if Postgres main port is 5432 and session_pool_ports is 2, "  
                "then ports 5433 and 5434 will be used for connection pooling.")  
  },  
  &SessionPoolPorts,  
  0, 0, MAX_SESSION_PORTS,  
  NULL, NULL, NULL  
},  

2、如果是postgres库,不使用pool模式,使用dedidate server模式。

区分是否postgres库的代码

src/backend/tcop/postgres.c

/* Serve all conections to "postgres" database by dedicated backends */  
if (SessionPoolSize != 0 && strcmp(dbname, "postgres") == 0)   // 连接postgres,一律使用dedicate server, 方便DBA用户上去维护 (在所有pool backend process都activate时,保证能连接数据库)   
{  
        elog(LOG, "Backend is dedicated");  
        SessionPoolSize = 0;  
        closesocket(SessionPoolSock);  
        SessionPoolSock = PGINVALID_SOCKET;  
}  
/* Assign session for this backend in case of session pooling */  
if (SessionPoolSize != 0)  
{  
        MemoryContext oldcontext;  
        ActiveSession = (SessionContext*)calloc(1, sizeof(SessionContext));  
        ActiveSession->memory = AllocSetContextCreate(TopMemoryContext,  
                                                                                                   "SessionMemoryContext",  
                                                                                                   ALLOCSET_DEFAULT_SIZES);  
        oldcontext = MemoryContextSwitchTo(ActiveSession->memory);  
        ActiveSession->id = CreateSessionId();  
        ActiveSession->port = MyProcPort;  
        ActiveSession->eventSet = FeBeWaitSet;  
        BackendPort = MyProcPort;  
        MemoryContextSwitchTo(oldcontext);  
}  

测试PG内置连接池是什么模式(transaction 模式)

1、创建一个普通用户与库

create role digoal login;  
create database digoal owner digoal;  

2、目前内置连接池的POOL模式为事务级 pool。同一个backend process,某个活跃会话的事务执行结束后,对应backend process的资源即可给同一backend process上的其他session利用。

3、设置为只有1个BACKEND PROCESS

session_pool_size=1  
  
重启数据库  

4、创建测试表

create table a (id int, info text);  
  
insert into a values (1,'test');  

5、SESISON A:

查看它的backend process的pid, 同时开启一个事务

digoal=> select pg_backend_pid();  
 pg_backend_pid   
----------------  
          56112  
(1 row)  
  
digoal=> begin;  
BEGIN  
digoal=> select * from a;  
 id | info   
----+------  
  1 | test  
(1 row)  

6、SESISON B:

连接数据库,堵塞,因为只有1个backend process,并且这个backend process当前繁忙。

psql -p 4001 digoal digoal  
  
hang  

7、SESISON A:

结束会话

end;  

8、SESISON B:

连接成功,查看它的backend process的pid,与session a的backend process的pid一致,所以会话A与B是共用一个backend process的。

digoal=> select pg_backend_pid();  
 pg_backend_pid   
----------------  
          56112  
(1 row)  

9、SESISON A:

开启事务

digoal=> begin;  
BEGIN  
digoal=> select * from a;  
 id | info   
----+------  
  1 | test  
(1 row)  

10、SESISON B:

执行SQL处于等待状态

digoal=> select count(*) from pg_stat_activity ;  
  
hang  

结论:Postgrespro pool模式为transaction模式,事务结束后,这个backend process才能给映射到这个backend process的其他会话使用。

目前的版本:session一定映射到一个backend process后,就不能漂移给其他的backend process,所以以上CASE,即使我们有多个shared backend process,实际上SESSION B也不能用到其他空闲中的backend process,因为它不能漂移到其他的backend process。

postgres pool版本目前存在的一些问题

问题1

discard all 释放同一个backend process下的所有变量,并不是当前session自己的变量,所以会导致绑定到这个backend process的所有session的变量丢失。

例如造成其他会话已经创建的prepared statements丢失,异常。

测试

digoal=> \h discard  
Command:     DISCARD  
Description: discard session state  
Syntax:  
DISCARD { ALL | PLANS | SEQUENCES | TEMPORARY | TEMP }  

1、SESSION A:

digoal=> select pg_backend_pid();  
 pg_backend_pid   
----------------  
          56112  
(1 row)  
  
digoal=> prepare p1 (int) as select * from a where id=$1;  
PREPARE  
digoal=> execute p1(1);  
 id | info   
----+------  
  1 | test  
(1 row)  

2、SESSION B:

digoal=> select pg_backend_pid();  
 pg_backend_pid   
----------------  
          56112  
(1 row)  
  
digoal=> execute p1(1);  
ERROR:  prepared statement "p1" does not exist  

discard all

digoal=> discard all;  
DISCARD ALL  

3、SESSION A:

digoal=> execute p1(1);  
ERROR:  prepared statement "p1" does not exist  

问题2

ctrl_c退出会话,会导致数据库crash , recovery.

这个用pgbench压测,并ctrl_c pgbench就可以发现问题

配置pgbench压测支持超过1000个连接

1、编译pgbench,支持超过1000个测试连接,参考如下方法

《PostgreSQL pgbench 支持100万连接》

用一个新的PostgreSQL编译一下pgbench,conn_pool版本的pg 11版本可能太老,没有办法融合这个pgbench的patch

wget https://ftp.postgresql.org/pub/snapshot/dev/postgresql-snapshot.tar.bz2  
tar -jxvf postgresql-snapshot.tar.bz2  
cd postgresql-11devel  

patch pgbench参考 《PostgreSQL pgbench 支持100万连接》

假设我把它编译到了 pgsql11/bin/pgbench ,可以拷贝到conn_pool版本的bin目录中

cp pgsql11/bin/pgbench ./pgsql11_pool/bin/  

用pgbench压测,可以校验一下前面提到的,如果session数超过pool的backend process数量,那么多个session 会映射到同一个backend process

当discard all时,影响的是同一个backend process下的所有session

测试

修改配置  
session_pool_size=56  
session_pool_ports=0  
  
重启数据库  

压测,开启8000个连接。

pgbench -i -s 20 -U digoal digoal  
  
pgbench -M prepared -n -r -P 1 -p 4001 -c 8000 -j 8000 -T 12000 -U digoal digoal  

压测过程中,discard all

SESSION A:

psql -p 4001 -U digoal digoal  
  
discard all;  

观察到pgbench 报错的自有一部分连接

progress: 460.0 s, 53016.7 tps, lat 38.635 ms stddev 4.520  
client 1253 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 929 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 873 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 1264 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 369 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 201 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 593 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 152 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 257 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 602 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 295 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 518 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 456 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 761 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 763 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 90 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 817 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 998 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 1993 aborted in command 4 (SQL) of script 0; ERROR:  prepared statement "P0_4" does not exist  
  
client 1624 aborted in command 4 (SQL) of script 0; ERROR:  prepa
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
4月前
|
Web App开发 Java Linux
Nexus【部署 02】最新版本 nexus-3.35.0-02-unix.tar.gz 安装配置启动及测试(JDK版本+虚拟机参数配置说明)
Nexus【部署 02】最新版本 nexus-3.35.0-02-unix.tar.gz 安装配置启动及测试(JDK版本+虚拟机参数配置说明)
116 0
|
6月前
|
前端开发
什么是 Mock 测试?掌握 Mock 测试的核心原理
Mock 的意思就是,当你很难拿到源数据时,你可以使用某些手段,去获取到跟源数据相似的假数据,拿着这些假数据,前端可以先行开发,而不需要等待后端给了数据后再开发。
|
6月前
|
传感器 数据采集 数据挖掘
无线振弦采集仪高低温试验箱测试原理
无线振弦采集仪是一种用来测量结构物动力学特性的仪器,它可以通过振弦传感器采集到结构物的振动信号,并通过数据分析,得到结构物的自然频率、阻尼比、振型等信息。为了确保无线振弦采集仪的准确性和可靠性,需要进行高低温试验,以验证它在各种环境下的性能。
无线振弦采集仪高低温试验箱测试原理
|
6月前
|
传感器 数据采集
工程监测仪器无线振弦采集仪高低温试验箱测试原理
无线振弦采集仪和高低温试验箱是两个独立的设备,但是它们可以结合使用来进行振弦测试。高低温试验箱是一种可以控制温度和湿度的设备,用于模拟各种环境下的物体性能变化,包括机械性能、电气性能等。无线振弦采集仪则是一种用于测量物体振动状态的设备,通常包括振动传感器和数据采集模块。
工程监测仪器无线振弦采集仪高低温试验箱测试原理
|
7月前
|
安全 网络协议 网络安全
Web安全性测试系列(二)DDOS拒绝服务攻击原理详解
Web安全性测试系列(二)DDOS拒绝服务攻击原理详解
|
9月前
|
小程序
fiddler系列之-fiddler对手机测试版本的小程序进行抓包
fiddler对手机测试版本的 http 请求的小程序进行抓包
|
12月前
|
关系型数据库 MySQL 数据库连接
Qt+MySql开发笔记:Qt5.9.3的msvc2017x64版本编译MySql8.0.16版本驱动并Demo连接数据库测试
mysql驱动版本msvc2015x32版本调好, mysql的mingw32版本的驱动上一个版本编译并测试好,有些三方库最低支持vs2017,所以只能使用msvc2017x64,基于Qt5.9.3,于是本篇编译mysql驱动的msvc2017x64版本,满足当前的特定需求,这次过程有点费劲,可能是Qt的版本低于Qt5.12,继续无保留分享出来。   本篇主要描述Qt5.9.3 msvc2017x64 + mysql8.0.16的驱动编译过程。
|
12月前
|
光互联 网络架构 异构计算
BOSHIDA 三河博电科技 模块电源高低温试验箱测试原理
电源模块是可以直接贴装在印刷电路板上的电源供应器,其特点是可为专用集成电路(ASIC)、数字信号处理器 (DSP)、微处理器、存储器、现场可编程门阵列 (FPGA) 及其他数字或模拟负载提供供电。一般来说,这类模块称为负载点 (POL) 电源供应系统或使用点电源供应系统 (PUPS)。由于模块式结构的优点甚多,因此模块电源广泛用于交换设备、接入设备、移动通讯、微波通讯以及光传输、路由器等通信领域和汽车电子、航空航天等。
|
12月前
BOSHIDA 模块电源高低温试验箱测试原理
有着26年历史的,专业生产电源,提供高质量的工业电源、军工电源、医疗电源、可编程电源并为用户提供定制及OEM代工电源服务。专业生产电源模块,性能稳定可靠,设计简单布局合理,只需一个电源模块,配上少量分立元件,即可获得电源。
|
关系型数据库 MySQL 数据库
Qt+MySql开发笔记:Qt5.9.3的mingw32版本编译MySql8版本驱动并Demo连接数据库测试
之前特定的mysql版本msvc版本已经调通了,但是为了更好的跨平台,所以选择用mingw32版本,于是需要编译mysql驱动的mingw32版本的驱动库,以便提供给qt连接mysql使用。
Qt+MySql开发笔记:Qt5.9.3的mingw32版本编译MySql8版本驱动并Demo连接数据库测试