PostgreSQL 数据库连接数设置的越大越好吗
为什么max_connections
设置过高可能对性能有害?
引言
作为一名 PostgreSQL 工程师,我们经常遇到的一个常见场景是,在一台相当强大的机器上系统运行缓慢。在这些情况下,我们经常看到max_connections
被设置为 10,000 或更多,有时甚至高达 30,000。虽然我们会建议max_connections
设置得太高,需要降低,但通常的回应是:“嗯,大多数连接都是空闲的,所以它们不应该影响性能。”这种说法是不正确的,因为一个空闲的连接并不是空气。在这篇文章中,我们将探讨为什么高空闲连接数可能对数据库性能有害。
数学
对于数据库管理员(DBA)或系统管理员(sysadmin)(或应用程序开发者)来说,配置 PostgreSQL 以应对预期的连接负载是很自然的。如果在项目团队之间的讨论后,预期企业应用程序的客户数量将是 10,000,那么max_connections = 10000
就会被编码进postgresql.conf
。
然而,似乎这些值往往是由业务部门决定的,而不一定由那些对硬件有更深入了解的人决定。例如,在许多企业数据中心,数据库服务器可能配备了 128 个 CPU 核心。开启超线程后,这台机器可以处理 256 个进程。我们通常期望一个核心上的舒适负载大约是 4 个进程。如果我们把这放在一个公式里,它应该看起来像这样:max_connections = #cores * 4
在这台大型企业机器的情况下,256 * 4 = 1024,所以应该设置不超过 1024。即使这个值在社区中也备受争议,许多专家认为它不应该超过几百。
“但是连接都是空闲的!”
虽然直觉上认为设置max_connections = 30000
并承诺绝大多数连接将是空闲的应该是无害的,但我鼓励支持高设置的人更深入地思考拥有如此多连接的含义。特别是,我们必须记住 PostgreSQL 是一个基于进程的应用程序,这意味着底层操作系统需要执行上下文切换来运行查询并执行与硬件的基本接口。在 CPU 数量高的繁忙系统上,存在受到缓存行争用影响的风险,我之前写过关于这个问题的文章。
即使操作系统能够轻松地同时管理数千个进程,Postgres 的监管进程(即 postmaster)也需要跟踪它因传入连接而 fork 的每个进程/后端。这种由postmaster
管理的成本可能会变得昂贵,因为非空闲查询需要获取一个快照,了解什么是可见/不可见的,已提交/未提交的(也就是事务隔离),这需要扫描进程列表及其快照信息。进程列表越大,调用GetSnapshotData()
所需的时间就越长。
一个简单的例子
为了说明这一点,我组织了一个非常基础的测试,基本上做以下几件事:
打开一个连接
运行
SELECT 1
保持连接打开,直到程序结束
在运行这个简单的程序以保持数千个空闲连接的同时,我在旁边运行了pgbench
并收集了 tps 输出(带有标志--client=10 --transactions=5000
)。这个测试的结果如下:
当然,这是一台较旧的机器(在 CentOS 7 上运行的 32-CPU VM,有 128GB RAM 的机械硬盘),但我认为它仍然有能力说明增加空闲连接数量的影响。我们可以看到,当空闲连接数量增加时,吞吐量下降。我运行了两次测试,结果相当相似。最后出现的峰值可能与缓存或其他后台活动有关。我故意在两次测试中保持autovacuum = on
,因为真实系统很可能会开启 autovacuum。对于第三次测试,我关闭了 autovacuum,虽然性能略有提升(因为活跃进程不再与 autovacuum 竞争 I/O 资源),但我们仍然看到扩展空闲连接会对性能产生负面影响。
如何实现高吞吐量
如果max_connections
不能设置为几百以上,我们如何在非常繁忙的企业级应用程序上实现高吞吐量?解决这个问题的最简单方法之一是使用连接池工具,如 pgbouncer,它将允许成千上万的应用程序连接共享一个相对较小的数据库会话池。这样做的一个优点是,因为max_connections
可以保持较低,管理员可以更慷慨地分配work_mem
,因为每个较少的进程可以获得更大的内存池份额。解决繁重的客户端应用程序流量的其他方法包括利用复制与 HAProxy 的混合实现读扩展。
结论
我们已经简要探讨了连接扩展如何对数据库性能产生负面影响。如果不想管理复杂的 PostgreSQL 配置,只想使用一下数据库,这里可以在线免费创建数据库:MemFireDB
评论