Postgresql - 准备好的语句与连接池 - 这是一个权衡吗?

Postgresql - prepared statements vs connection pooling - is it a tradeoff?

据我了解,您可以在 Postgresql 中使用准备好的语句或连接池(使用 pgPool/pgBouncer 等工具),但同时只能从其中一个中受益 (at least with Npgsql driver for .NET, plus )。我对吗?
如果是这样 - 对于其他运行时和语言是否也是如此,例如 Java、Python、Go?还是特定于实施的问题?

我认为这是一个笼统的问题,所以我会给出一个笼统的答案。哪些方面适用于特定的连接池实现可能会有所不同。

连接池有几种模式:

  • 一个线程在会话期间保持连接(会话池):
    在这种情况下,可以在会话期间保持持久状态(如准备好的语句),但您应该在会话返回池时清除状态。

  • 线程在数据库事务(事务池)期间保持连接:
    在那种情况下,您必须在每次交易后清理状态,因此准备好的语句没有多大意义。

  • 一个线程在一个语句(语句轮询)期间保留一个连接:
    这仅在非常有限的情况下有用,在这些情况下,您不需要跨越多个语句的事务。显然,不能共享准备好的语句之类的状态。

这取决于你使用什么样的连接池。基本上,线程保持连接的时间越长,使用准备好的语句就越有意义。

当然,如果你知道自己在做什么,你也可以在建立数据库连接后立即创建一个准备好的语句,永远不要释放它。这仅在所有线程都需要相同的准备语句时才有效。这样的设置很容易搞砸。

这是一个复杂的问题,但这里有一些答案。

如@laurenz-albe 所写,您可以使用 pgbouncer 和准备好的语句,但需要使用会话池。这允许您在连接期间使用准备好的语句(即只要您的 NpgsqlConnection 实例处于打开状态)。但是,如果您处于短暂的连接场景中(例如,为每个 HTTP 请求打开和关闭连接的 Web 应用程序),您就不走运了。从这个意义上说,可以说池化语句和准备语句不兼容。

但是,如果您使用 Npgsql 的内部池机制(默认情况下启用)而不是 pgbouncer,那么您准备好的语句会自动跨连接持久保存 open/close。换句话说,当你调用NpgsqlCommand.Prepare()时,如果物理连接恰好已经准备好SQL,那么准备好的语句就会被重用。这样做是专门为了释放准备好的语句在短期连接场景中的速度优势。这是 Npgsql 的一个非常独特的行为,see the docs for more info

这是进程内连接池的优点之一,与 pgbouncer 等进程外连接池相反 - Npgsql 在传递时保留有关物理连接的信息,在本例中为table 准备了哪些语句(名称和 SQL)。