安装
可以直接使用
- yum install pgbouncer
安装(rpm管理的是1.14版本)
或者在http://www.pgbouncer.org/downloads/ 下载最新的tat.gz包
解压出来进入目录,通过
- ./configure --prefix=/home/pgbouncer
- make & make install
安装(官网最新为1.16版本)
依赖问题自行解决,这里不赘述。
建议通过yum安装,简单快捷,不用处理依赖问题。
通过yum安装成功后,会自动生成pgbouncer账户,为了后面的执行 需要进入 /etc/passwd 修改sbin/nologin 为 /bin/bash
服务部署位置建议:
- 如果只有单应用访问,建议PgBouncer部署在应用服务器上,减少应用服务器和pgbouncer之间的开销
- 如果有多应用访问,建议PgBouncer部署在数据库侧,减少pgbouncer和数据库之间的开销
- 也可以部署成多实例模式,针对读和写进行分别部署pgbouncer,因为pgbouncer本身只是数据库连接池,不具备负载均衡,或高可用,IP漂移等特性,需要结合其他成熟产品进行组合使用
Quick-start
1、创建或编辑/etc/pgbouncer/pgbouncer.ini文件,典型内容如下:
[databases]``themis = host=``127.0``.``0.1` `port=``5432` `dbname=themis``[pgbouncer]``logfile =
/var/log/pgbouncer/pgbouncer.log``pidfile = /var/run/pgbouncer/pgbouncer.pid``listen_addr = *``listen_port = ``6688``auth_type =
trust``auth_file = /etc/pgbouncer/userlist.txt``admin_users = admin``stats_users = admin``pool_mode = session``max_client_conn =
``100``default_pool_size = ``20``server_reset_query = RESET ALL; SET SESSION AUTHORIZATION DEFAULT
tips:
- listen_addr:监听地址,表示哪些地址能访问这个pgbouncer服务,*表示所有
- auth_type:一般选择trust或md5,前者明文存储密码,后者md5加密密码
- auth_file:需要创建一个用户列表文件,只有在文件中的用户才允许访问连接池
- admin_users:pgbouncer的管理员账号,需要在userlist.txt中存在
- pool_mode:默认为会话池模式(Session pooling),还可以选择事务池(Transaction pooling)和语句池(Transaction pooling)
- max_client_conn:连接池最多允许多少连接
- default_pool_size:每一对数据库-用户允许多少连接
- server_reset_query:当一个连接被返回给连接池时,它必须被重置为刚创建的连接的状态。在把一个连接返回给连接池之前,PgBouncer通过发出一个查询来实现这种重置。PG使用DISCARD ALL命令,不过GreenPlum不支持这个命令,使用示例中的命令来达到这个目的
模式比较:
2、创建userlist.txt文件,需要与pgbouncer.ini里的auth_file路径相对应
如果auth_type = trust,则内容为
"user_name" "password"
如果auth_type = md5,则内容为
"user_name" "md5 + MD5(<password><username>)"
可以通过查询GP的pg_catalog.pg_shadow视图获得,导出即可
pgbouncer也提供了一个mkauth.py工具,可以直接生成userlist文件,这里不详细描述了。
3、启动
pgbouncer -d /etc/pgbouncer/pgbouncer.ini
4、停止
可以直接kill进程来停止pgbouncer
cat /var/run/pgbouncer/pgbouncer.pid | xargs kill -9
也可以登陆pgbouncer后台数据库,执行shutdown来关闭
5、登陆数据库
通过连接池连接目标数据库
psql -h 192.168.4.11 -p 6688 -U admin xxx
连接pgbouncer数据库
psql -p 6688 -U cyadmin pgbouncer
配置项详解
配置文件分为[databases] 和 [pgbouncer]两部分,现在我们来详细讲解一下里面的配置。
[databases]部分的参数配置,都是以键值对的形式出现的,例如dbname、host、port、user、password,这几个参数都比较好理解。还有几个额外的配置我们接着看一下:
(1) pool_size 用来配置连接池的大小。连接池的含义上面说过,如果没有这个值则使用[pgbouncer]部分的default_pool_size的值。
(2)connect_query :后面跟一个SQL语句字符串,用于探测这个连接是否正常,如果执行SQL语句出错,则换一个连接。
(3)client_encoding:指定连接的客户端使用的字符集编码
(4)datestyle:指定日期类型
(5)timezone:指定时区。
[pgbouncer]部分的配置
这个部分的配置项比较多,主要分为下面几类:
-
通用配置项
-
日志配置项
-
控制界面访问控制配置项
-
连接健康检查和超时配置项
-
危险的超时配置项
-
底层网络配置项
通用配置
logfile 指定日志文件,默认值是/var/log/pgbouncer/pgbouncer.log
pidfile 指定pid文件位置,默认值是/var/run/pgbouncer/pgbouncer.pid
listen_addr 监听的地址,默认值是127.0.0.1,可以使用*号表示监听所有IP地址。
listen_port 监听的端口,默认值是6432
unix_socket_dir 指定unix socket文件的目录,默认为/tmp目录
unix_socket_mode 指定unix socket文件的权限,默认值为0777
unix_socket_group 指定unix socket文件的组,默认无
user 指定启动PgBouncer的用户名,windows系统不支持此设置
auth_type 认证的类型,默认是trust,其他值包括md5,crypt,plain,any,hba。用的较多的是md5
auth_file 认证文件的位置,默认值是/etc/pgbouncer/userlist.txt
pool_mode 指定池的模式,默认是session模式,还可以是transaction和statement
max_client_conn 允许的最大连接数
default_pool_size 默认的池大小,默认值20
min_pool_size 最小的池大小,每个连接池至少会向后端数据库保持多少个连接
reserve_pool_size 连接池的保留连接数
reserve_pool_timeout 保留连接的超时时间
server_round_robin 负载均衡的方式是否设置为“round robin”,默认为关闭,即后进先出
ignore_startup_parameters 默认情况PgBouncer只会跟踪一些默认参数,并且能检测这些参数的变化,保持这些参数和客户端的一致。在这个配置后面跟的配置后被PgBouncer忽略,不会被检查。
disable_pqexec 是否禁止简单查询协议,默认值为0。简单查询协议允许一个请求发送多个SQL,容易导致SQL注入攻击。
日志配置项:
syslog 是否打开syslog,windows下打开eventlog,默认值为0,表示不打开。
syslog_ident 默认为PgBouncer
syslog_facility
log_connections 是否记录连接成功的日志,默认值为1,表示记录
log_disconnections 是否记录断开连接的日志,默认值为1,表示记录
log_pooler_errors 连接池法网客户端的错误是否记录在日志中,默认值为1,表示记录
stats_period 把汇总的统计信息写入日志的时间周期,默认是60s
控制界面访问控制配置项:
admin_users 管理用户名,默认值是postgres
stats_users 允许连接到控制界面,查看连接池只读信息的用户列表。可以执行除“SHOW FDS”以外的其他“SHOW”命令
server_reset_query
server_check_delay 空闲的连接多长时间进行一次健康检测,判断连接是否可用。如果设置为0,则立即检测,默认值为30s
server_check_query,进行健康检查的SQL语句,如果为0,表示不检测,默认值为“select 1;”
server_lifetime 连接的存活时间,连接超过这个时间就会被关闭,默认为3600,设置为0表示只使用一次。
server_idle_timeout 连接的idle时间,超过此时间,连接会被关闭。默认为600
server_connect_timeout 后端数据库的login时间超过这个值就会被关闭。默认为15s
server_login_retry 传教到后端数据库的连接失败后,等多长时间后重试,默认为15s
client_login_timeout 客户端与PgBouncer建立连接后,如果无法在这段时间内完成登录,那么连接会断开,默认为60s
危险超时配置项
指的是为防止一些未知错误或者原因导致系统卡住的针对性配置。
query_timeout 允许超过该时间值的SQL会被断开,应该比SQL实际的执行时间稍长,也需要比数据库的statement_timeout参数值更大。为了应付一些未知的网络问题。默认为0.0,禁止使用
query_wait_timeout 请求在队列中等待被执行的最长时间,如果超过该时间还没有分配到连接,就会断开。默认为0,禁止使用。
client_idle_timeout 客户端连接空闲超过该时间,则断开连接。默认值为0,禁止使用
idle_transaction_timeout 客户端启动事务后,超过这个时间没有结束事务,则关闭这个客户端连接。默认值为0,禁止使用
底层网络连接配置
pkt_buf 用于网络包的内部缓冲区大小,会影响发出的TCP包的大小即内存的使用,默认值为2048,一般保持默认值
max_packet_size 通过PgBouncer最大的包大小,包可以是一个SQL,也可以是返回的结果,默认值是2147483647
listen_backlog TCP监听函数listen的Backlog参数,默认值为128
sbuf_loopcnt 处理过程中,每个连接处理多少数据就切换到下一个连接。默认为5,如果设置为0,表示不限制。不限制时,一个连接发送大量数据,另外的连接可能就会空闲,导致被结束掉。
tcp_defer_accept linux下,默认为45,其他平台为0。详细解释用man 7 tcp来查看
tcp_socket_buffer 默认没有设置
tcp_keepalive 是否以操作系统的默认值打开基本的keepalive 设置,在linux下,操作系统的keepalive里,默认值时tcp_keepidle=7200, tcp_keepintvl-75,tcp_keepcnt=9,其他操作系统类型,默认值为1
tcp_keepcnt 默认未设置
tcp_keepidle 默认未设置
tcp_keepintvl 默认未设置
连接测试
先上结论:
连接池机制在建立连接上,能节约30%的时间开销。
但如果连接建立后执行复杂sql,则主要的资源消耗在查询上,连接开销较小
sql越复杂,连接开销越可以忽略
测试方案一:连接建立后执行一次简单查询
循环建立连接1000次,连接池(会话池)能节省15.9%的时间开销
直连测试结束,结束时间:2021-10-11 20:17:02.339198,用时214秒
连接池测试结束,结束时间:2021-10-11 20:20:03.121827,用时180秒
测试了循环10000次,结论类似
测试方案二:只建立连接,马上断开
循环建立1000次,连接池(会话池)能节省32.1%的时间开销
直连测试结束,结束时间:2021-10-12 14:31:07.778456,用时53秒
连接池测试结束,结束时间:2021-10-12 14:31:43.784784,用时36秒
测试脚本:
import psycopg2
import datetime
def gp_direct():
conn = psycopg2.connect(dbname = 'xxx',
user = 'xxx',
password = 'xxx',
host = '192.168.4.11',
port = '5432')
cur = conn.cursor()
s = """select * from dw.ods_active;"""
cur.execute(s)
cur.fetchall()
cur.close()
conn.close()
return None
def gp_pool():
conn = psycopg2.connect(dbname = 'xxx',
user = 'xxx',
password = 'xxx',
host = '192.168.4.11',
port = '6688')
cur = conn.cursor()
s = """select * from dw.ods_active;"""
cur.execute(s)
cur.fetchall()
cur.close()
conn.close()
return None
n = 1000 # 测试连接次数
start_time = datetime.datetime.now()
print('直连测试开始,起始时间:',start_time)
for i in range(n):
gpD = gp_direct()
print('第{}次直接连接'.format(i + 1))
end_time = datetime.datetime.now()
ss = (end_time - start_time).seconds
print('直连测试结束,结束时间:{},用时{}秒'.format(end_time,ss))
print('-------------------------------------------------')
start_time = datetime.datetime.now()
print('连接池测试开始,起始时间:',start_time)
for i in range(n):
gpP = gp_pool()
print('第{}次连接池连接'.format(i + 1))
end_time = datetime.datetime.now()
ss = (end_time - start_time).seconds
print('连接池测试结束,结束时间:{},用时{}秒'.format(end_time,ss))
print('---------------------------------------------------')