PostgreSQL 服务器端准备语句的生命周期是多少

What's the life span of a PostgreSQL server-side prepared statement

根据PostgreSQL documentation,准备好的语句绑定到数据库session/connection:

PREPARE creates a prepared statement. A prepared statement is a server-side object that can be used to optimize performance. When the PREPARE statement is executed, the specified statement is parsed, analyzed, and rewritten. When an EXECUTE command is subsequently issued, the prepared statement is planned and executed.

Prepared statements only last for the duration of the current database session. When the session ends, the prepared statement is forgotten, so it must be recreated before being used again.

但是,Markus Winand (author of SQL Performance Explained) says that:

PostgreSQL does not have a shared query plan cache, but it has an optional query plan cache for prepared statements. That means that the developer has the choice to use a prepared statement with or without cached query plan. But note that the cache is dropped when the prepared statement is closed.

哪个是真的?

只要数据库连接打开,准备好的语句就一直存在吗?所以当使用连接池时,只要连接池没有显式关闭物理连接,或者服务器端准备好的语句是JDBC PreparedStatement 关闭后即被清除。

我认为两者都是正确的,但怀疑 PostgreSQL 文档通常比我更真实。但是,在这里我认为 PostgreSQL 文档可能不准确。

大概应该是这样写的:

Prepared statements only last until it is DEALLOCATEed and no longer than the duration of the current database session.

未经检查,我坚信 JDBC 驱动程序会在 JDBC PreparedStatement 关闭时释放服务器端准备好的语句。

所以,您的问题最终归结为“java.sql.PreparedStatement 如何与 PostgreSQL 一起玩”。请参阅最后关于“这如何与服务器准备的计划一起玩”的答案。

答案如下:这取决于您使用的 JDBC 驱动程序。

TL;DR:在现代驱动程序中,服务器准备的语句一直存在,直到连接终止或语句被另一个语句逐出(常规 LRU 逐出)。

注意:PostgreSQL 服务器无法跨数据库连接共享准备好的语句,因此最好的 JDBC 驱动程序可以做的是在每个连接中缓存计划。

注意:JDBC 规范要求使用 ?, ? 作为绑定占位符,而服务器需要 , 因此 JDBC 驱动程序缓存所谓的解析 SQL还有短信。

有两个著名的 JDBC 驱动程序:pgjdbc 和 pgjdbc-ng

pgjdbc

https://github.com/pgjdbc/pgjdbc

因为 pgjdbc 9.4-1202 使用 PreparedStatement 时它会自动缓存服务器端计划。 注意:即使您 close() PreparedStatement,语句也会被缓存。 为了进入服务器端准备,您需要执行查询 5 次(可以通过 prepareThreshold 配置)。

目前,缓存是按连接实现的。默认情况下,pgjdbc 缓存 256 (preparedStatementCacheQueries) 个查询和最多 preparedStatementCacheSizeMiB 个查询。这是一个保守的设置,因此您可能需要对其进行调整。有关属性的说明,请参阅 documentation。 缓存包括已解析和服务器准备的语句。​​

github 问题:https://github.com/pgjdbc/pgjdbc/pull/319

pgjdbc-ng

https://github.com/impossibl/pgjdbc-ng

我不喜欢 pgjdbc-ng,但看起来它同时进行解析(默认缓存大小为 250 queries) and server-preparing (default cache size is 50 查询)。服务器端准备语句的支持于 2014 年 2 月 24 日登陆,因此如果您使用较新的版本,您可以获得语句缓存。

注意:如果您不小心使用了很长的查询,您可以点击 OutOfMemory,因为 pgjdbc-ng 无法根据保留的字节数逐出条目。

缓存是针对每个连接的,因此即使您关闭语句也会透明地使用它。

关于 pgjdbc-ng 的性能,我不能说太多,尽管自从上次我试图向它抛出 jmh 以来,它因随机异常而失败。

github 问题:https://github.com/impossibl/pgjdbc-ng/pull/69

服务器准备计划

PostgreSQL 有 PREPAREDEALLOCATE 命令在通过网络发送 EXEC 时引用语句。它优化了两件事:

  1. 当使用 PREPAREd 语句(换句话说,服务器准备的语句)时,客户端不必一次又一次地发送查询文本。它只是发送一个简短的查询名称和绑定变量的值。
  2. 从 9.2 开始,数据库仍然会尝试重新计划查询的前几次执行。它这样做是为了尝试查询是否需要多个计划,或者通用计划是否足够好。最终(如果查询没有参数则立即),数据库 might switch to a generic plan.
  3. 从 12 开始,有一个设置可以强制使用通用或自定义计划执行所有服务器准备的语句:plan_cache_mode=auto | force_custom_plan | force_generic_plan

换句话说,PreparedStatement优化了JDBC端的查询解析和数据库端的查询规划。

更多信息在这里:http://blog.endpoint.com/2014/04/custom-plans-prepared-statements-in.html

PL/pgSQL

中的准备语句

根据文档,PostgreSQL caches 计划在 PL/pgSQL 中使用查询。这发生在几次执行后(3 或 5,我不记得确切的阈值),所以在你创建存储过程后它可能会有点慢,但是它会切换到缓存计划(前提是数据库同意使用通用计划对于特定查询)。

换句话说,为了实现“缓存执行计划”,您要么需要使用最新的 JDBC 驱动程序,要么可以将所有查询包装到存储过程中。 对过程的调用将在每次执行时重新计划,但是调用本身通常比组成过程的查询短得多。