除了节省连接和断开连接的开销之外,连接池可以将大量的客户端连接汇集到少量的实际数据库连接。在 PostgreSQL 中,活动数据库连接的最佳数量通常在((2 * core_count) + Effective_spindle_count)左右。超过这个数字,吞吐量和延迟都会变得更糟。 注意:最近的版本提高了并发性,所以在 2022 年我会推荐类似((4 * core_count) + effective_spindle_count)的东西。
有时人们会说“我要支持 2000 个用户,响应时间快”。几乎可以保证,如果您尝试使用 2000 个实际数据库连接来执行此操作,性能将非常糟糕。如果您有一台具有四个四核处理器的机器并且活动数据集被完全缓存,那么通过将请求汇集到大约 35 个数据库连接,您将看到这 2000 个用户的性能要好得多。
要理解为什么这是真的,这个思想实验应该会有所帮助。考虑一个假设的数据库服务器机器,它只有一个资源可以共享——一个内核。该核心将在所有并发请求之间平等地进行时间切片,而不会产生开销。假设有 100 个请求同时进入,每个请求都需要一秒钟的 CPU 时间。核心对所有这些都起作用,在它们之间进行时间切片,直到它们都在 100 秒后完成。现在考虑如果您在前面放置一个连接池会发生什么情况,该连接池将接受 100 个客户端连接,但一次只向数据库服务器发出一个请求,将在连接繁忙时到达的所有请求放入队列中。现在当 100 个请求同时到达时,一个客户端在 1 秒内得到响应;另一个在 2 秒内得到响应,最后一个客户端在 100 秒内得到响应。没有人需要等待更长的时间才能得到响应,吞吐量是一样的,但平均延迟是 50.5 秒而不是 100 秒。
一个真正的数据库服务器有更多的资源可以并行使用,但同样的原则是,一旦它们饱和,你只会通过添加更多的并发数据库请求来伤害事情。它实际上比示例更糟糕,因为任务越多,任务切换越多,锁和缓存争用增加,L2 和 L3 缓存行争用,以及许多其他会影响吞吐量和延迟的问题。最重要的是,虽然高work_mem
设置可以通过多种方式帮助查询,但该设置是每个连接的每个计划节点的限制,因此对于大量连接,您需要将其保持在非常小以避免刷新缓存或甚至导致交换,这会导致计划变慢或哈希表溢出到磁盘之类的事情。
一些数据库产品有效地在服务器中构建了一个连接池,但是 PostgreSQL 社区的立场是,由于最好的连接池是在更接近客户端软件的地方完成的,他们将把它留给用户来管理。大多数池化程序将有一些方法将数据库连接限制为一个硬数字,同时允许更多的并发客户端请求,并根据需要对它们进行排队。这就是你想要的,它应该在事务的基础上完成,而不是每个语句或连接。