所以,您的问题最终归结为“java.sql.PreparedStatement 如何使用 PostgreSQL”。最后请参阅“这如何与服务器准备的计划一起使用”的答案。
答案如下:这取决于您使用的 JDBC 驱动程序。
TL;DR:在现代驱动程序中,服务器准备的语句一直存在,直到连接终止或语句被另一个语句驱逐(常规 LRU 驱逐)。
注意:PostgreSQL 服务器不能跨数据库连接共享准备好的语句,因此最好的 JDBC 驱动程序可以做的是在每个连接中保持计划缓存。
注意:JDBC 规范要求将 ?, ? 用于绑定占位符,而服务器需要 $1, $2,因此 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 查询)和服务器准备(默认缓存大小为 50 查询)。对服务器端预处理语句的支持于 2014 年 2 月 24 日登陆,因此如果您使用较新的版本,您可以获得语句缓存。
注意:如果您不小心使用了很长的查询,您可以点击OutOfMemory,因为 pgjdbc-ng 无法根据保留字节数驱逐条目。
缓存是每个连接的,因此即使您关闭语句,它也会被透明地使用。
关于 pgjdbc-ng 的性能我不能说太多,尽管自从上次我尝试向它抛出 jmh 时它失败并出现随机异常。
github问题:https://github.com/impossibl/pgjdbc-ng/pull/69
服务器准备的计划
PostgreSQL 有PREPARE 和DEALLOCATE 命令在通过网络发送EXEC 时引用该语句。它优化了两件事:
- 当使用
PREPAREd 语句(换句话说,服务器准备的语句)时,客户端不必一次又一次地发送查询文本。它只是发送一个简短的查询名称和绑定变量的值。
- 自 9.2 起,数据库仍会尝试重新计划查询的前几次执行。这样做是为了尝试查询是否需要多个计划或通用计划是否足够好。最终(如果查询没有参数,则立即),数据库might switch to a generic plan。
- 从 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 驱动程序,要么可以将所有查询包装到存储过程中。
对过程的调用将在每次执行时重新计划,但调用本身通常比构成过程的查询短得多。