【问题标题】:COUNTs slow down a query 150+ timesCOUNT 使查询减慢 150 多次
【发布时间】:2015-02-19 22:58:20
【问题描述】:

我有一个 JPAQL-2-mySQL 查询在超时 5 分钟后停止运行:

select 
      question, 
      ans_items, 
      count(distinct ans_items.id) as voteCount, 
      (select 
            count(distinct ans.id) 
         from 
            answers ans 
               join ans.session ses  
         where 
                ses.survey.id = :surveyId 
            and ses.state = :sessionState 
            and ans.question.id = question.id 
         group by 
            question.id) as total, 
      max(ranks.rank) as rmax 
   from 
      Session as ss 
         join ss.answers as answers 
            join answers.items as ans_items 
            join answers.question as question 
            left join question.ranks as ranks 
   where 
          ss.survey.id = :surveyId 
      and ss.state = :sessionState 
   group by 
      question.id, 
      ans_items.variant.id, 
      ans_items.variantWeight  
   order by 
      question.orderNumber asc

如果我用常量值替换 COUNT:

 select question, ans_items, 150, 
 (select 10 from answers ans join ans.session ses
 where ses.survey.id=:surveyId and ses.state=:sessionState 
 and ans.question.id=question.id group by question.id) as total, 
 max(ranks.rank) as rmax 
 from Session as ss 
 join ss.answers as answers 
 join answers.items as ans_items 
 join answers.question as question 
 left join question.ranks as ranks 
 where ss.survey.id=:surveyId and ss.state=:sessionState 
 group by question.id, ans_items.variant.id, ans_items.variantWeight  
 order by question.orderNumber asc

,它会在 2 秒后开始执行。这快了 150 倍以上。似乎几个 COUNT 会大大降低查询速度!有没有办法优化查询,或者我绝对必须不使用数据库来计算计数(我可以这样做,但我想避免这种重写)。

【问题讨论】:

  • 您查看过每个查询的 EXPLAIN 输出吗?
  • JOIN 通常带有ON...
  • @Brewal 是 JPAQL-to-mySQL,ON 是自动插入的……手动插入会影响性能吗?
  • 我的错……我不这么认为。我们能看到实际生成的 SQL 吗?
  • 我猜,问题不是来自多个计数,而是来自列部分中的子选择。这会对每个结果行进行选择,这可能是降低查询速度的原因。您可以通过保留简单的 count(...) as voteCount 并仅将子选择替换为常量值来验证这一点。

标签: mysql performance jpa


【解决方案1】:

看起来OKis所以恐怕你需要调试一下SQL。 您需要查看 JPA 发送的实际查询是什么,然后在 DB 中使用 Explain 来查看执行计划是什么(如果您获得全表扫描或其他内容)。这种查询应该只使用索引来完成,所以它们会很快(如果你当然有正确的索引)。因此,一旦您看到执行计划,您就可以找出缺少哪些索引。

要获取查询输出,请更改持久性中的日志级别,例如在 eclipselink 中:

 <property name="eclipselink.logging.level" value="FINE"/>

然后保护你调用 count 方法与许多 \n\n 轻松发现它。

它将具有一种准备好的语句形式,例如 SELECT COUNT(id)... where Sesion.ID = ?,并且在另一行下方将使用实际值。

替换 ?使用这些值,您就有了正确的 SQL。 连接到您的数据库,并检查执行计划,在 mysql 中您调用 EXPLAIN SELECT COUNT....

如果这是您第一次,您将需要查看文档如何理解输出,但在每个输出行的底线,您应该会看到正在使用的索引,否则您会遇到麻烦。

【讨论】:

    【解决方案2】:

    ans_items.id 是否已编入索引?如果没有,请在其上创建索引。如果它是索引的一部分,则无济于事,除非索引中的第一列是 ans_items.id

    【讨论】:

      【解决方案3】:

      您被淹没的原因是您没有连接 ON 子句来识别表之间的关系。因此,对于一个表中的每条记录,它都连接到另一个表中的每条记录,并且该结果再次连接到下一条记录。

      请更新您的查询以识别相关的“ID”。并使用别名限定所有列...每个别名的字段来自哪里。

      例如:使用您的字段查询不同的 ans.id(适用于别名),

       from 
          answers ans 
             join ans.session ses  
      

      答案和会话之间的关系是什么。

              answers ans 
                 join ans.session ses  
                    on ans.sessionID = ses.sessionID
      

      同样在您的主要查询中......除了特定调查和状态的 where 子句之外,会话 (ss) 与答案及其与 ans_items 和 ans 项目与问题的关系如何。解决这些问题,您将获得更好的结果。

         from 
            Session as ss 
               join ss.answers as answers 
                  join answers.items as ans_items 
                  join answers.question as question 
                  left join question.ranks as ranks 
      

      一旦知道这些,就可以提供额外的索引来优化查询以提供进一步的帮助。

      【讨论】:

      • 查看问题下方的 cmets,这是 JPA 而不是普通的 MySQL。
      • @OlafDietsche 是的,JPAQL 中似乎没有 ON。至少,我在手册中没有找到。
      • 没有ON?使用WHERE。 (您在 ONWHERE 中没有与 ans_items 相关的内容。)
      【解决方案4】:

      compound INDEX(survey_id, state) 应该有助于提高速度。 (或者这两个可能是相反的顺序)。

      如果您使用 InnoDB,请检查 innodb_buffer_pool_size 是否约为可用 RAM 的 70%。

      但也许@Brewal 搞定了——你没有用于连接答案或项目或问题或等级的 ON 子句。因此,您不必要地使用这些表的所有组合创建一个巨大的 tmp 表。删除 GROUP BY 以查看它。如果 JPAQL 有罪,那就摆脱它!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-26
        • 1970-01-01
        相关资源
        最近更新 更多