介绍
虽然开发和暂存环境可以帮助您预测在生产环境中将面临的许多情况,但有些挑战只有在大规模时才会开始出现。数据库连接管理恰好属于此类:来自客户端实例的请求数量可以迅速超过数据库软件支持的连接限制。
为了解决这种资源竞争,需要连接管理策略和工具,以防止长时间排队、请求失败和影响用户错误。连接池是一种基于部署中间队列系统来管理和回收数据库连接的策略,通常被成功地用于缓解这些问题。
在本指南中,我们将讨论什么是连接池、它试图解决哪些特定条件以及它是如何工作的。我们将介绍一些流行的实现作为代表性示例,并讨论它们如何在客户端请求超过数据库可用连接时改变客户端的行为。
连接池是 Prisma Data Platform 上的 Prisma Accelerate 提供的核心功能之一。如果您使用 Prisma 与数据库交互,请启动一个免费项目,轻松管理您的连接并浏览您的数据。
打开数据库连接涉及哪些步骤?
在讨论连接管理一般情况和连接池特别是,查看数据库连接过程中发生的事情可能会有所帮助。
对于客户端应用程序,要打开到数据库的连接,必须执行许多步骤。对于每个连接,必须执行以下步骤中的部分或全部步骤
连接数量如何影响数据库服务器资源?
除了完成所有上述步骤所需的时间之外,每个连接还需要资源来建立和维护。例如,在 PostgreSQL 中,一些测试的工作负载导致每个连接使用 1.5-14.5MB 的内存。
随着连接数量的增加,CPU 使用率也会增加,因为服务器必须管理每个新连接的状态。随着更多内存和 CPU 用于管理连接,它们会开始影响其他方面,例如 事务执行率。管理大量连接的开销开始干扰数据库系统最佳缓存结果的能力,并降低其执行有用工作的能力。
连接数量如何影响客户端应用程序?
虽然上一段描述了数据库服务器必须支付的连接成本,但对客户端也有一些直接影响。数据库服务器只能配置为接受一定数量的连接。当达到该限制时,额外的连接请求将被拒绝。
这意味着,默认情况下,您的客户端代码需要实现逻辑,以使用指数退避算法重复请求,以处理这些失败。更重要的是,您的客户端可能会被迫频繁地使用该功能,导致查询停滞、延迟和问题,这些问题很快就会传递给您的用户。
如果没有中间组件进行协调,您将被迫考虑
- 增加数据库服务器的连接限制(影响数据库服务器的内存和 CPU 使用率以及事务速率),
- 扩展数据库以分配更多内存、CPU 或网络容量,或者
- 扩展数据库以将请求分发到更多机器上
这些选择本身并不一定不好,但它们对于我们在这里描述的拥塞类型来说可能是过度的。连接池是一种替代方案,它可以帮助您使用当前的资源做更多的事情。
什么是连接池?
连接池是一种策略,它涉及为多个请求循环使用数据库连接,而不是在查询解决后立即关闭它们。通常,这是通过在数据库服务器及其客户端应用程序之间引入一个名为连接池器的软件来完成的,该连接池器负责管理两者之间的连接。
如前所述,在建立连接时,必须执行相当长的一系列操作,然后数据库服务器才能实际运行查询。连接池器试图通过在初始查询后保持连接打开并重用它来运行其他查询来分摊这些操作的成本。
在这个系统中,客户端连接到连接池器,而不是直接连接到数据库。客户端将池器视为数据库本身,池器解释查询以将适当的连接传递给客户端。需要注意的是,在客户端和池器之间打开和关闭连接仍然存在开销。但是,这些连接通常具有较低的开销,因为大多数繁重的进程发生在建立与数据库本身的连接时。
连接池是如何工作的?
连接池器负责代表客户端打开、关闭和维护与数据库的连接。它通过遵循类似于缓存系统可能使用的算法来做到这一点。
当客户端连接到连接池器并请求连接时,池器会快速评估请求特征。它可能会查看诸如数据库用户、将要执行的特定操作、加密类型或访问的数据库对象之类的信息。
一旦获得此信息,它就会查看其可用连接池,以查看是否有任何现有连接可用于运行新请求。如果找到合适的可用连接,它会将其传递给客户端并允许它通过连接运行其查询。如果没有适合运行新请求的连接存在于池中,它将使用所需的参数打开与数据库的新连接,并将该连接传递给客户端。
客户端照常使用连接执行其查询。当查询完成后,池器不会终止连接,而是将连接放回池中,以便后续查询可以潜在地重用它。池器可以使用它选择的任何算法(自建立以来的时间、自上次使用以来的时间等)异步地垃圾收集池中的连接。
内部池和外部池有什么区别?
从广义上讲,连接池是指在多个请求过程中维护连接的算法。这可以在客户端应用程序内部或外部实施,使用外部工具或服务。
连接池的内部实现通常是数据库驱动程序、ORM(对象关系映射器)或可能集成到客户端应用程序中的其他数据库客户端的功能。这些解决方案通过维护与数据库服务器的长期运行连接并在代码库中为多个查询重用这些连接,提供了连接池的一些好处。
虽然内部连接池很有用,但它确实有一些实际限制。正在执行的每个应用程序实例通常必须维护自己的池。这会影响连接可以在查询之间共享和重用的范围,因为每个池只服务于单个应用程序实例。
另一种解决方案是实现外部连接池。这种方法部署了一个独立的软件,它可以与多个客户端实例通信并为其池化连接。虽然这种部署方案确实引入了额外的网络跳跃,但它通常提供了额外的可扩展性和灵活性。连接池器可以与数据库服务器一起部署以服务于许多不同的客户端,或者与客户端应用程序一起部署以服务于单个服务器上运行的任何应用程序实例。
一些常见的外部连接池器是什么?
有许多连接池器可用于不同的数据库系统。我们可以看一下一些针对 PostgreSQL 可用的实现,以更好地了解不同的解决方案如何解决这个问题。
pgbouncer
也许最著名的 PostgreSQL 连接池器是 pgbouncer
.
创建于 2007 年,pgbouncer
的重点是为管理 PostgreSQL 连接提供轻量级的池化机制。它在部署位置和执行池化的方式方面提供了相当大的灵活性。
对于客户端连接短暂的情况,该 项目建议在 Web 服务器上部署 pgbouncer
客户端代码将在其中执行。将池器与客户端软件一起放置允许两者之间的连接使用比 TCP 更轻量级的机制,从而减少延迟。对于需要将来自许多不同客户端的连接池化在一起的场景,您可以改为与数据库服务器一起部署。
使用 pgbouncer
时最重要的决定之一是选择要使用哪种池化模式。三种可用选项是
- 事务池化:连接在客户端执行完每个事务后被撤销。对于任何后续事务,将再次分配连接。这允许
pgbouncer
快速回收连接,而客户端可能在事务之间执行其他操作。 - 会话池化:连接在客户端与其池器的连接持续时间内分配给客户端。这意味着每个客户端连接都与与数据库的专用连接配对。当客户端会话结束时,连接仍然被重用,但可以同时使用池器的客户端数量大大减少。
- 语句池化:连接被分配来执行单个语句。这会导致连接的快速分配和释放,这允许许多客户端使用有限数量的连接,但可能会破坏事务语义并在某些情况下导致意外行为。
在大多数情况下,事务池化在回收空闲连接、管理相当数量的客户端以及维护有关数据库会话和事务语义的预期行为方面提供了最佳平衡。
pgpool
另一个 PostgreSQL 连接池器是 pgpool-II
,通常简称为 pgpool
。虽然 pgbouncer
是一款专注于连接池化的轻量级工具,但 pgpool
提供了更多相关功能的选择。
除了连接池化,pgpool
还支持在多个后端数据库实例之间负载均衡查询,并且有一个看门狗服务来启用高可用性操作以实现自动故障转移。此外,它还提供了一个管理 GUI,对于某些部署场景非常有用。
尽管具有这些高级功能,但 pgpool
通常被认为在实际管理连接池化方面更有限。虽然 pgbouncer
允许三种池化模式,但 pgpool
只能使用与会话模式等效的模式运行,这意味着连接仅在客户端断开连接时重新分配。这会降低 pgpool
可以处理的客户端数量。
结论
在本指南中,我们了解了连接池的概念以及如何使用它来帮助减少数据库负载和资源消耗。我们概述了一些客户端和数据库之间建立连接可能很昂贵的原因,并描述了如何重用连接可以降低后续查询的成本。之后,我们讨论了连接池器是如何工作的,并查看了 PostgreSQL 生态系统中的一些代表性池器示例。
随着您的应用程序扩展和查询负载变得更加复杂,数据库连接限制会很快导致问题。虽然无法完全消除与连接管理相关的开销,但连接池器是维护性能和增加数据库可以服务的客户端数量的宝贵工具。
连接池是 Prisma Data Platform 上的 Prisma Accelerate 提供的核心功能之一。如果您使用 Prisma 与数据库交互,请启动一个免费项目,轻松管理您的连接并浏览您的数据。