【问题标题】:Reduced my SQL by 40% but it runs 60 times slower?将我的 SQL 减少了 40%,但运行速度慢了 60 倍?
【发布时间】:2014-01-29 10:51:20
【问题描述】:

我有JPA 生成的 SQL,在最坏的情况下有 1250 行。

我的查询结构是嵌套在查询的 WHERE 语句中的 20 个子查询。此查询在 0.015 秒内运行。

我试图优化我的查询,因为我注意到我在子查询中重用了很多连接(例如,两个子查询的区别仅在于它们的 WHERE 语句)。这将 SQL 减少到 750 行和 12 个子查询,但由于某种原因,它需要 0.9 秒才能运行。

有什么可以解释的吗?当有更多可用数据时,我让查询运行得更快的尝试实际上会运行得更快吗?

谢谢

【问题讨论】:

  • where子句中包含的字段是否有索引?
  • 这是一个严肃的魔法。提供你的sql代码、表结构和查询计划怎么样?

标签: mysql sql hibernate jpa


【解决方案1】:

由于问题中提供的信息有限,我只能推测为什么在您的特定情况下执行时间会增加,但总而言之,更少的代码并不等于更快的查询。

“简化”查询可能导致执行时间延长的主要原因之一是简化意味着不再使用索引,因为虽然查询可能看起来更易于阅读,但您实际上是在要求优化器执行更复杂的操作.

想象一下这个简单的架构:

CREATE TABLE T1 (ID INT AUTO_INCREMENT PRIMARY KEY, A INT);
CREATE TABLE T2 (ID INT AUTO_INCREMENT PRIMARY KEY, A INT, B INT);

CREATE INDEX IX_T2_A ON T2 (A);
CREATE INDEX IX_T2_B ON T2 (B);

现在,假设我有以下查询:

SELECT  COUNT(T1.ID)
FROM    T1
        INNER JOIN
        (   SELECT  ID
            FROM    T2
            WHERE   A IN (1, 10)
            UNION 
            SELECT  ID
            FROM    T2
            WHERE   B IN (1, 10)
        ) T2
            ON t2.ID = t1.ID;

您可能会认为,这可以“简化”为删除UNION,如下所示:

SELECT  COUNT(T1.ID)
FROM    T1
        INNER JOIN
        (   SELECT  ID
            FROM    T2
            WHERE   A IN (1, 10)
            OR      B IN (1, 10)
        ) T2
            ON t2.ID = t1.ID;

但是,通过结合您的标准,您已确保不会使用任何索引(T2.AT2.B),因为优化器试图同时执行这两个索引。因此,将执行全表扫描,而不是使用您已有的两个索引,并且根据数据的分布情况,这可能会更加昂贵。

这在运行EXPLAIN时得到确认:

ID      SELECT_TYPE     TABLE       TYPE    POSSIBLE_KEYS   KEY     KEY_LEN     REF     ROWS    FILTERED    EXTRA
1       PRIMARY         <derived2>  system  (null)          (null)  (null)      (null)  1       100     
1       PRIMARY         T1          const   PRIMARY         PRIMARY     4       const   1       100         Using index
2       DERIVED         T2          index   IX_T2_A         IX_T2_A     5       (null)  1       100         Using where; Using index
3       UNION           T2          index   IX_T2_B         IX_T2_B     5       (null)  1       100         Using where; Using index
(null)  UNION RESULT    <union2,3>  ALL         (null)  (null)      (null)  (null)  (null)      (null)  


ID      SELECT_TYPE TABLE       TYPE    POSSIBLE_KEYS   KEY     KEY_LEN     REF     ROWS    FILTERED    EXTRA
1       PRIMARY     <derived2>  system  (null)          (null)  (null)      (null)  1       100     
1       PRIMARY     T1          const   PRIMARY         PRIMARY 4           const   1       100     Using index
2       DERIVED     T2          ALL     IX_T2_A,IX_T2_B (null)  (null)      (null)  1       100     Using where

Example on SQL Fiddle

【讨论】:

    【解决方案2】:

    如果查询运行得那么快,那么它不会处理很多行。

    很可能是使用索引处理连接。引擎可以只在内存中加载索引并处理查询——甚至可能永远不会触及原始数据。

    当您在没有 where 子句的情况下实现连接时,会发生一些事情:

    1. 您知道读取和写入额外数据的开销;
    2. 正在处理的数据量可能大于任何子查询中的数据量;
    3. 不会进行其他优化(甚至可能会消除大部分处理)。

    【讨论】:

      【解决方案3】:

      也许那些额外的行创建的表格(或选择的数据更少)比您拥有的那些更小,因此您可以更快地比较表格(数据)。但是现在,由于您减少了较小表的数量并可能增加了较大表的大小,当您运行特定查询时,查询必须在较大的表中分页(更多数据),因此它们多花点时间。

      更多数据进行比较 = 更多处理时间

      【讨论】:

      • 是的,谢谢,这就是问题所在。事实证明,虽然一些子查询共享一些连接,但我合并它们的方式涉及加入一个新表,以便新的子查询可以拥有它需要的所有连接。这意味着新查询中的每个 WHERE 语句都必须在比以前更大的表中分页。
      猜你喜欢
      • 1970-01-01
      • 2021-03-03
      • 2016-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-15
      相关资源
      最近更新 更多