为什么 time_bucket_gapfill 在第 10 个请求时始终抛出 PSQLException?
Why does time_bucket_gapfill consistently throw a PSQLException, at 10th request?
我有一个 Spring 引导应用程序,其中有一个 REST 服务,我在其中使用了 JdbcTemplate。然后我有一个 PostgreSQL 和存储数据的 TimescaleDB(verson 2.3.0)。
在我的一个端点中,我调用以下代码从客户端本地时区的数据库中获取一些时间戳:
SELECT time_bucket_gapfill(CAST(:numOfHours * INTERVAL '1 hour' AS INTERVAL),
timestamp AT TIME ZONE 'UTC' AT TIME ZONE :timezone,
:start AT TIME ZONE :timezone, :end AT TIME ZONE :timezone) AS time
FROM info
WHERE timestamp >= :start AND timestamp < :end
GROUP BY time
当我调用该特定端点时,前 9 次 returns 数据完美无缺,然后在第 10 次,它抛出以下 SQL 错误:
ERROR: invalid time_bucket_gapfill argument: start must be a simple expression
Note that explicitly provided start
and stop
or derived from WHERE clause values need to be simple expressions. Such expressions should be evaluated to constants at the query planning. For example, simple expressions can contain constants or call to now()
, but cannot reference to columns of a table.
他们想说的是这些参数必须是常量。所以你不能在这里使用参数。
为什么这对前 10 次执行有效是因为 JDBC 驱动程序和 PostgreSQL 处理这些参数的方式:
对于 JDBC java.sql.PreparedStatement
的前 5 次执行,PostgreSQL 驱动程序将参数插入到查询中并发送一个简单的查询字符串
从第 6 次执行开始,JDBC 驱动程序认为值得在 PostgreSQL
中创建命名准备语句
在该准备语句的前五次执行期间,PostgreSQL 生成一个使用实际参数值
的自定义计划 ]
只有从第六次执行开始,PostgreSQL才会考虑一个generic plan,其中参数是占位符
执行此通用计划会导致 TimescaleDB 错误。
所以有几种补救措施:
不要在那里使用参数。这是最好和最可靠的解决方案。但是,那么就不能使用prepared statement,而是每次都要构造query string (dynamic SQL).
-
The driver uses server side prepared statements by default when PreparedStatement
API is used. In order to get to server-side prepare, you need to execute the query 5 times (that can be configured via prepareThreshold
connection property). An internal counter keeps track of how many times the statement has been executed and when it reaches the threshold it will start to use server side prepared statements.
这允许您通过将 prepareThreshold
设置为 0 来解决该问题,但代价是性能较差。
将 PostgreSQL 参数 plan_cache_mode
设置为 force_custom_plan
以避免使用通用计划。这可能会对您的整体表现产生负面影响。
这三种解决方案都降低了准备语句的有效性,但这是您要解决 TimescaleDB 的这一限制所必须付出的代价。
我有一个 Spring 引导应用程序,其中有一个 REST 服务,我在其中使用了 JdbcTemplate。然后我有一个 PostgreSQL 和存储数据的 TimescaleDB(verson 2.3.0)。
在我的一个端点中,我调用以下代码从客户端本地时区的数据库中获取一些时间戳:
SELECT time_bucket_gapfill(CAST(:numOfHours * INTERVAL '1 hour' AS INTERVAL),
timestamp AT TIME ZONE 'UTC' AT TIME ZONE :timezone,
:start AT TIME ZONE :timezone, :end AT TIME ZONE :timezone) AS time
FROM info
WHERE timestamp >= :start AND timestamp < :end
GROUP BY time
当我调用该特定端点时,前 9 次 returns 数据完美无缺,然后在第 10 次,它抛出以下 SQL 错误:
ERROR: invalid time_bucket_gapfill argument: start must be a simple expression
Note that explicitly provided
start
andstop
or derived from WHERE clause values need to be simple expressions. Such expressions should be evaluated to constants at the query planning. For example, simple expressions can contain constants or call tonow()
, but cannot reference to columns of a table.
他们想说的是这些参数必须是常量。所以你不能在这里使用参数。
为什么这对前 10 次执行有效是因为 JDBC 驱动程序和 PostgreSQL 处理这些参数的方式:
对于 JDBC
java.sql.PreparedStatement
的前 5 次执行,PostgreSQL 驱动程序将参数插入到查询中并发送一个简单的查询字符串从第 6 次执行开始,JDBC 驱动程序认为值得在 PostgreSQL
中创建命名准备语句在该准备语句的前五次执行期间,PostgreSQL 生成一个使用实际参数值
的自定义计划 ]只有从第六次执行开始,PostgreSQL才会考虑一个generic plan,其中参数是占位符
执行此通用计划会导致 TimescaleDB 错误。
所以有几种补救措施:
不要在那里使用参数。这是最好和最可靠的解决方案。但是,那么就不能使用prepared statement,而是每次都要构造query string (dynamic SQL).
-
The driver uses server side prepared statements by default when
PreparedStatement
API is used. In order to get to server-side prepare, you need to execute the query 5 times (that can be configured viaprepareThreshold
connection property). An internal counter keeps track of how many times the statement has been executed and when it reaches the threshold it will start to use server side prepared statements.这允许您通过将
prepareThreshold
设置为 0 来解决该问题,但代价是性能较差。 将 PostgreSQL 参数
plan_cache_mode
设置为force_custom_plan
以避免使用通用计划。这可能会对您的整体表现产生负面影响。
这三种解决方案都降低了准备语句的有效性,但这是您要解决 TimescaleDB 的这一限制所必须付出的代价。