为什么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