ZQPool是中启乘数科技发布的PostgreSQL数据的开源连接池软件。主要解决PostgreSQL生态中流行的连接池软件pgbouncer软件的一些缺点。
软件架构应用程序连接到ZQPool,ZQPool连接到PostgreSQL数据库。
安装教程安装方法如下:
把发行版本zqpool1.0.x86_64.xz拷贝到一个目录中解压:tarxfzqpool1.0.x86_64.xz同时把zqpool.conf也拷贝到此目录中解压后有两个文件zqpool.conf和zqpool,其中zqpool.conf是配置文件,做相应的配置。然后启动./zqpool即可编译的方法:
gitclonehttps://gitee.com/csudata/zqpoolcdzqpoo,lmake使用说明配置zqpool.conf文件,各个配置项说明如下:
listen_port=5436:设置zqpool的监听端口listen_addr=*:设置zqpool的监听IP,设置为*,表示在本地的所有IP地址上监听default_pool_size=10:设置到后端数据库的总连接数max_client_conn=3000:允许应用程序连接zqpool的总连接数db.1.user=u01:第一个数据库的用户名db.1.dbname=postgres:第一个库的数据库名称#db.1.ipport=192.168.56.39:5432,192.168.0.34:5432db.1.ipport=172.22.224.10:5432:第一个数据库的IP和端口db.1.passwd=u01:目前用户的密码
上面的db.1代表第一个数据库,还可以有db.2、db.3等多个数据库。db.1.ipport后面可以配置多个IP地址端口,以逗号分隔,如果配置了多个则随机均衡到后面的多个IP地址上。
exampleconfig:
zqpool.conf:
listen_port=5436listen_addr=*default_pool_size=10max_client_conn=3000db.1.user=u01db.1.dbname=postgresdb.1.ipport=172.22.224.10:5432db.1.passwd=u01启动zqpool:
[codetest@pgdevzqpool]$./zqpool2022/05/2409:12:30server.go:2188:Startingserveron:5436...在另一个窗口中,使用psql连接zqpool的5436端口:
[codetest@pgdevzqpool]$/usr/pgsql-10/bin/psql-h172.22.224.10-p5436-Uu01-dpostgresPasswordforuseru01:psql(10.20,server10.5)Type"help"forhelp.postgres=>\\dListofrelationsSchema|Name|Type|Owner--------+--------+-------+----------public|test01|table|postgres(1row)postgres=>select*fromtest01;id|t----+---(0rows)postgres=>insertintotest01values(1,'111'),(2,'222'),(3,'333');INSERT03postgres=>select*fromtest01;id|t----+-----1|1112|2223|333(3rows)使用数据库连接池的主要目的有两个:
减少到数据库上的连接数。应用程序到连接池软件上有M个连接,这M个连接不是同时都繁忙的,这M个连接上同一个时刻发来的并发SQL可能只有N个(N通常大大小于M),这样连接池软件只需要在后端数据库上建N个连接。就可以满足了要求。这个场景通常是java应用。我们可以想象一个场景:一个java应用可能部署在200台主机上,而每个主机上java应用自身会开启一个java连接池,这个java连接池假设开20个连接,这时到数据库上就有200*20=4000个连接,这些连接实际上多数时间都是空闲的,少数时间才是活跃的。4000个连接,PostgreSQL数据库就需要启动4000个进程,太多连接会降低数据库的效率。减少断连接应用花在新建数据库连接的时间。PostgreSQL数据库对每一个连接需要fork出一个新的进程来提供服务,而每次fork一个进程是需要时间的。而连接池软件可以预先建好到数据库的连接,应用程序连接到连接池软件后,连接池软件可以从池中取一个已经建好的连接马上提供服务,这样就大大减少了新连接的时间。这个场景的典型应用是php应用。php应用到数据库通常是短连接。而PostgreSQL数据库中流行的pgbouncer通常解决不了上面的第一个问题(java应用):即减少到数据库上连接的目的。
要减少到数据库上的连接数,pgbouncer连接池的模式只能配置成语句级或事务级,不能配置成会话级。因为pgbouncer在会话级下,前面来多少个连接,到数据库也必须建多少个连接,起不到减少数据库连接的目的。
当我们把pgbouncer配置成语句级或事务级时,java应用连接pgbouncer会报错:
org.postgresql.util.PSQLException:ERROR:preparedstatement"S_1"alreadyexists这个原因是jdbc执行SQL是分两个步骤的:
先使用Prepare的SQL,即:“prepareS_1asselect*fromtest01whereid=$1;”然后再“executeS1(1);”当“prepareS_1asselect*fromtest01whereid=$1;”这条SQL来的时候,从连接池中那个一个连接A,执行了后,就释放了此连接;
这时来了另一个SQL,可能从连接池中取到的还是之前的连接A,然后再执行“prepareS_1asselect*fromtest02whereid=$1;”,
但这个prepareSQL的名字S_1已经被前面的SQL占用,这时就报上面的错了。
注:jdbc实际的行为比上面的更复杂,但原理大致就是上面描述的这个过程。
而ZQPool通过记录一个连接上的PrepareSQL的名字,并替换成不重复的名字的方式解决了这个问题。
pgbouncer还有一个缺点,处理SQL的转发只能用到CPU的一个核,即pgbouncer是单线程程序。对于高并发的情况下,超过单核的性能时,就立即回出现瓶颈。而ZQPool是使用golang的协程技术,可以利用了多核的性能,下面是我们在一台物理机上做的测试:
这是pgbouncer的测试情况:
[postgres@csyun01~]$pgbench-h10.197.160.18-p6432-Uu01 -S-P2 -T30-c32pgbench(14.3)startingvacuum...end.progress:2.0s,30407.5tps,lat1.050msstddev0.180progress:4.0s,30108.6tps,lat1.062msstddev0.182progress:6.0s,30231.5tps,lat1.058msstddev0.179progress:8.0s,31157.9tps,lat1.026msstddev0.176progress:10.0s,30491.7tps,lat1.049msstddev0.178progress:12.0s,30463.0tps,lat1.050msstddev0.180progress:14.0s,30366.2tps,lat1.053msstddev0.179progress:16.0s,30177.5tps,lat1.060msstddev0.180progress:18.0s,30067.1tps,lat1.064msstddev0.181progress:20.0s,30420.1tps,lat1.051msstddev0.177.........这是使用ZQPool测试的情况:
[postgres@csyun01~]$pgbench-h10.197.160.18-p5436-Uu01 -S-P2 -T30-c32Password:pgbench(14.3,server10.5)startingvacuum...end.progress:2.0s,111134.7tps,lat0.213msstddev0.058progress:4.0s,112688.1tps,lat0.209msstddev0.058progress:6.0s,114570.8tps,lat0.207msstddev0.054progress:8.0s,107305.3tps,lat0.216msstddev0.066progress:10.0s,108680.1tps,lat0.215msstddev0.063progress:12.0s,108867.6tps,lat0.214msstddev0.064.........可以看到ZQPool的tps可以到10万每秒,而pgbouncer最多到3万每秒就上不去了。
评论