【问题标题】:Kill slow queries in REST API Java Spring Boot杀死 REST API Java Spring Boot 中的慢查询
【发布时间】:2018-03-05 15:58:26
【问题描述】:

我们正在使用以下技术设计 API Rest:

  • spring-boot 1.5.7
  • spring-data-jpa
  • MySQL 5.5.59

这是一个带有 spring-boot 的 Java REST API 设计,并通过 mysql-connector 连接到 MySQL 数据库。存储库中的大多数查询都是用 JPA Query 方式编写的。 为了编译它,我们使用 maven 并将其部署在运行在 tomcat 服务器中的 fatjar 中。

事实上,对于一个用户来说,其中的多个表和资源可能非常大。所以当 GET 请求的偏移量较大时,我们可以处理慢查询。

我们尝试了几种解决方案,例如:

  • 设置 mysql 连接的 tomcat 参数
  • 使用 hikari 但没有可用参数来终止慢查询
  • 在 glassfish5 服务器中部署其余 api,但 spring data jpa 查询会引发 execption(无法提取结果集),所以这是个问题。

此外,很多人会说我“使用分页并检查请求中的偏移量”。事实是我们正在使用它,但是类 PageRequest 有一个棘手的行为。实际上,API 通过此类返回给您的资源具有以下形状:

{
  "content": {},
  "links": [
    {
      "href": "string",
      "rel": "string",
      "templated": true
    }
  ],
  "page": {
    "number": 0,
    "size": 0,
    "totalElements": 0,
    "totalPages": 0
  }
}

如您所见,您有一个带有字段 totalElements 的对象 page。如果一个用户的表中有很多条目,这就是问题所在。因为 spring data 会做一个“SELECT COUNT(*)”来获得这个 totalElements 信息。分页不是解决方案,因为我们已经有了。

所以我们要问的是最佳实践,也许是在使用 spring-boot 的 Restful API 设计中处理和终止慢查询的解决方案。

谢谢!

【问题讨论】:

  • REST API 应该是无状态的。理论上这意味着它不应该保持会话状态或管理查询执行时间。
  • @RaymondNijland 是的,我同意,但是如果您的 API 是无状态的并且您在一个非常大的表上执行 GET 请求,mysql 将面临一个缓慢的查询,这对您的良好行为不利整个应用程序。因此,不是rest api本身的问题,而是关于spring的最佳配置以及与mysql的连接。
  • 有一个解决方案叫分页。
  • 您提到了偏移量,这似乎是您请求的参数。如何验证该参数的值并拒绝偏移超出范围的请求?
  • @MateuszMrozewski 发布关于分页的编辑 ;)

标签: mysql spring performance hibernate spring-boot


【解决方案1】:

关于分页看看这个答案:Way to disable count query from PageRequest for getting total pages?

基本上返回一个列表而不是一个页面。这当然不会给你总数,所以你需要以不同的方式处理这种情况。下一页可能需要一个额外的查询,该查询将返回一个空列表,但您将保存每一页的计数。

如果由于某种原因您仍然需要总数,则必须进行计数。

关于您的评论“由您的数据库引起的慢查询”:生成慢查询的不是数据库。慢查询是由您和您的代码生成的。除了终止正在运行的查询之外,您的问题可能还有其他解决方案,但您需要提供有关数据库的更多信息:结构、记录数、确切查询问题、存在哪些索引等。也许您可以引入缓存?

如果您仍然坚持终止查询,您可以在 CompletableFuture 中运行并等待超时完成:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html

【讨论】:

  • 你说得对,对不起,我误会了“数据库导致的慢查询”。为了让您恢复上下文,我们的客户发送短信,因此我们最大的客户每周发送数百万条短信。因此,例如表 MESSAGES 对他们来说可能很大,并且 PageRequest 的 SELECT COUNT(*) 很慢。我认为最佳实践就像您说的那样只提供有关是否有上一页或下一页的信息。 @M
  • 当然,只是想澄清一下以避免误解:) 看看答案是否有用。如果没有,则需要您提供更多信息。
  • 是的,您似乎不需要确切数量的记录,尤其是当您逐页显示记录时。我想在某种仪表板或报告中可能需要它,你需要一个计数。您还可以显示估计计数(每隔几分钟更新一次,不准确,但可能就足够了)。
  • 即使在联网平台上,如果消息表中没有totalElements和totalPages的信息也没有问题,只需要Previous和Next按钮!我必须找到一种方法来创建带有消息列表的资源,并添加一个带有下一页和上一页编号的对象(如果有的话)。
【解决方案2】:

经过大量研究,我找到了解决问题的方法,只需重点阅读 tomcat 数据源文档即可。我发现我在 application.yml 中设置的 jdbc 拦截器如下:

jdbc-interceptors: QueryTimeoutInterceptor(queryTimeout=20);SlowQueryReport(threshold=20000,logFailed=true)

所以这就是我想要的,例如,如果我的查询超过 20 秒就会抛出异常并杀死它!

感谢您的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-13
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 2018-07-06
    • 1970-01-01
    • 2023-03-06
    • 2021-08-28
    相关资源
    最近更新 更多