【问题标题】:Spring Boot JdbcTemplate - disable statement cache?Spring Boot JdbcTemplate - 禁用语句缓存?
【发布时间】:2021-02-04 11:00:59
【问题描述】:

我有一个使用 REST-API 与之通信的 Spring Boot 后端。此后端使用 Spring JdbcTemplate 连接并在 PostgreSQL 数据库上执行查询。

我发现了一个问题,即一个请求在重复 10 次后变得明显变慢。我已将问题缩小到使用JdbcTemplate 从数据库中检索数据的代码部分。更具体地说,问题发生在每个 tomcat 工作线程的第二次迭代:

[nio-8080-exec-1] --- GET /myresource - Execution time 400 ms
[nio-8080-exec-2] --- GET /myresource - Execution time 300 ms
[nio-8080-exec-3] --- GET /myresource - Execution time 285 ms
...
[io-8080-exec-10] --- GET /myresource - Execution time 200 ms

现在每个 tomcat worker 已经接收并处理了一个请求,下次这些 worker 中的一个接收到相同的请求时,使用完全相同的查询,执行时间要长 10-15 倍:

[nio-8080-exec-1] --- GET /myresource - Execution time 6000 ms
[nio-8080-exec-2] --- GET /myresource - Execution time 5500 ms
[nio-8080-exec-3] --- GET /myresource - Execution time 6700 ms

我尝试使用 psql 或 pgAdmin 运行相同的查询,没有问题。这使我相信JdbcTemplate 正在以某种方式为每个工作人员缓存查询,并且第二次运行查询时缓存会启动,并且由于某种原因它要慢得多,但我不确定。我也尝试将 tomcat 更改为 jetty/undertow,但同样的问题在那里发生,所以我相信它一定与 JdbcTemplate 有关。

有没有办法用JdbcTemplate 禁用这种类型的缓存,或者我可以做些什么来避免这种行为?

谢谢!

编辑:
我的 application.yaml:

spring:
    datasource:
        platform: postgres
        url: my-jdbc-url
        username: my-user
        password: my-password

代码根据请求中的参数动态创建带有 WHERE/AND 子句的查询,但相同的请求参数总是创建相同的查询。代码:

public List<MyDatatype> runQuery(MyParams params) {
    String sql = createSqlFromParams(params);
    List<Object> params = createParamsList(params);
    return jdbcTemplate.query(sql, params.toArray(), myDatatypeRowMapper());
}

查询最终看起来像这样(使用 postGIS 函数按坐标之间的距离排序):

SELECT * FROM my_table 
WHERE x IN [1,2,3] 
AND y BETWEEN 0 AND 1000
AND z BETWEEN 0 AND 500
ORDER BY geom <-> other_geom
LIMIT 1000;

编辑 2:
根据@M.Deinum 的建议,添加
spring.datasource.hikari.data-source-properties.preparedStatementCacheQueries=0 解决了这个问题!

【问题讨论】:

  • JdbcTemplate 不缓存任何内容,它只是javax.sql 包的包装。因此,如果有的话,这里起作用的是 JDBC 驱动程序本身或您的数据库设置(spring.datasource 属性)会影响这一点。
  • 您能否将您的application.properties 以及使用JdbcTemplate 的代码添加到您的问题中。数字 10 让我感到怀疑,因为这是用于连接的默认池大小,因此您实际上可能由于 JdbcTemplate 的使用不当而导致连接泄漏。如果您添加spring.datasource.hikari.maximum-pool-size=5,我有一个微弱的怀疑,它将在 5 个请求后发生。
  • @M.Deinum 感谢您的回复!我在问题中添加了更多信息。我尝试将最大池大小更改为 5,但在 10 个请求后问题仍然存在。
  • 尝试将spring.datasource.hikari.data-source-properties.preparedStatementCacheQueries=0 添加到application.properties 这将禁用Server Prepared Statements。可能是 GIS 扩展把事情搞砸了。另外您使用的是哪个驱动程序版本?
  • @M.Deinum 我正在使用 'org.postgresql:postgresql' 驱动程序,postgres 版本 12.3 和 postgis 版本 3.0.1。添加spring.datasource.hikari.data-source-properties.preparedStatementCacheQueries=0 解决了这个问题!非常感谢你的帮助!我已经更新了问题以反映这一点。我怎样才能让你的答案成为公认的答案?

标签: java spring postgresql spring-boot jdbctemplate


【解决方案1】:

假设您在 Spring Boot 中使用默认连接池 HikariCP,您可以使用 spring.datasource.hikari.data-source-properties 提供额外的驱动程序特定属性。

要禁用Server Prepared Statements,您需要包含preparedStatementCacheQueries 属性并将值设置为0(默认值为256)。

spring.datasource.hikari.data-source-properties.preparedStatementCacheQueries=0

这将禁用整个缓存,并可能影响应用程序的不同区域。

这些相关问题herehere 似乎暗示您可能想要检查磁盘、索引等,而不是禁用查询缓存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-30
    • 2016-08-16
    • 2014-11-30
    • 2018-03-21
    • 2017-01-25
    • 1970-01-01
    • 2016-10-13
    • 2016-02-23
    相关资源
    最近更新 更多