【问题标题】:Slow performance of combined MySQL queries组合 MySQL 查询的性能缓慢
【发布时间】:2016-05-10 08:34:29
【问题描述】:

我的这个查询有多个运行很慢的子查询。

SELECT DISTINCT pav.products_options_values_id, 
                pav.products_options_values_name, 
                pav.products_options_values_sort_order 
FROM            products_stock ps, 
                products_options_values pav, 
                ( 
                                SELECT DISTINCT pa.products_id, 
                                                pov.products_options_values_id, 
                                                pov.products_options_values_name, 
                                                pa.options_values_price, 
                                                pa.price_prefix 
                                FROM            products_attributes pa, 
                                                products_options_values pov, 
                                                ( 
                                                                SELECT DISTINCT p.products_image,
                                                                                p.products_quantity,
                                                                                p.products_status,
                                                                                m.manufacturers_id,
                                                                                p.products_id,
                                                                                p.products_date_added,
                                                                                p.products_subimage1,
                                                                                pd.products_name,
                                                                                p.products_price,
                                                                                p.products_length,
                                                                                p.products_width,
                                                                                p.products_height,
                                                                                p.products_tax_class_id,IF(s.status, s.specials_new_products_price, NULL)             AS specials_new_products_price,
                                                                                IF(s.status, s.specials_new_products_price, p.products_price)                         AS final_price,
                                                                                IF(p.clearance_price < p.products_cost*2.25, p.clearance_price, p.products_cost*2.25) AS sorting_price
                                                                FROM            products p 
                                                                LEFT JOIN       manufacturers m
                                                                using          (manufacturers_id)
                                                                LEFT JOIN       specials s 
                                                                ON              p.products_id = s.products_id
                                                                LEFT JOIN       products_attributes pa
                                                                ON              p.products_id = pa.products_id
                                                                LEFT JOIN       products_options po
                                                                ON              pa.options_id = po.products_options_id
                                                                LEFT JOIN       products_options_values pov
                                                                ON              pa.options_values_id = pov.products_options_values_id ,
                                                                                products_description pd,
                                                                                categories c,
                                                                                products_to_categories p2c
                                                                WHERE           p.products_status = '1'
                                                                AND             p.products_id = pd.products_id
                                                                AND             pd.language_id = '1'
                                                                AND             p.products_id = p2c.products_id
                                                                AND             p2c.categories_id = c.categories_id
                                                                AND             (( 
                                                                                                                pd.products_name LIKE '%a%'
                                                                                                OR              po.products_options_name LIKE '%a%'
                                                                                                OR              pov.products_options_values_name LIKE '%a%'
                                                                                                OR              pd.products_description LIKE '%a%') )
                                                                ORDER BY        p.products_id DESC) m
                                WHERE           m.products_id = pa.products_id 
                                AND             pa.options_id = 1
                                AND             pa.options_values_id = pov.products_options_values_id
                                AND             pov.language_id = '1' 
                                GROUP BY        pov.products_options_values_id 
                                ORDER BY        pov.products_options_values_sort_order ASC) q
WHERE           q.products_id = ps.products_id 
AND             ps.products_stock_attributes = concat('1-', pav.products_options_values_id) 
AND             ps.products_stock_quantity > 0 
ORDER BY        pav.products_options_values_sort_order ASC

这是解释结果:

尝试优化它几个小时,但我可能误读了 EXPLAIN 信息,因为无论我做什么,它似乎都无法让它更快,所以我在这里向专家寻求帮助。

它变得如此缓慢的原因是什么,我应该怎么做才能让它变快?

【问题讨论】:

  • 请注意,那里有 4 个条目表明查询正在执行全表扫描,因为索引不可用。在解释的第 1、2、4 和 7 行确保相关列(来自 where 子句)被正确索引。即,如果它是覆盖索引,则列在索引中的排序正确。

标签: mysql performance


【解决方案1】:

首先,我使用一致的 JOIN 子句与逗号列出的表来清理查询。接下来,您最里面的查询有一个显然没有被利用的顺序。如果不能优化,Order by 通常会导致查询延迟很大。

SELECT DISTINCT 
      pav.products_options_values_id,
      pav.products_options_values_name,
      pav.products_options_values_sort_order
   FROM
      products_stock ps
         JOIN products_options_values pav
            ON ps.products_stock_attributes = concat('1-', pav.products_options_values_id) 
            AND ps.products_stock_quantity > 0 
         JOIN ( SELECT DISTINCT 
                      pa.products_id, 
                      pov.products_options_values_id, 
                      pov.products_options_values_name, 
                      pa.options_values_price, 
                      pa.price_prefix 
                   FROM
                      products_attributes pa
                         JOIN products_options_values pov
                            ON pa.options_values_id = pov.products_options_values_id
                            AND pov.language_id = '1' 
                         JOIN ( SELECT DISTINCT 
                                      p.products_image,
                                      p.products_quantity,
                                      p.products_status,
                                      p.manufacturers_id,
                                      p.products_id,
                                      p.products_date_added,
                                      p.products_subimage1,
                                      pd.products_name,
                                      p.products_price,
                                      p.products_length,
                                      p.products_width,
                                      p.products_height,
                                      p.products_tax_class_id,
                                      IF(s.status, s.specials_new_products_price, NULL ) 
                                         AS specials_new_products_price,
                                      IF(s.status, s.specials_new_products_price, p.products_price )
                                         AS final_price,
                                      IF( p.clearance_price < p.products_cost * 2.25, p.clearance_price, p.products_cost * 2.25 ) 
                                         AS sorting_price
                                   FROM
                                      products p 
                                         JOIN products_description pd
                                            ON p.products_id = pd.products_id
                                            AND pd.language_id = '1'

THIS SECTION CAN BE REMOVED              JOIN products_to_categories p2c
                                            AND p.products_id = p2c.products_id
                                            JOIN categories c
                                               ON p2c.categories_id = c.categories_id
                                         LEFT JOIN manufacturers m
UP TO THIS LINE                              ON P.manufacturers_id = m.manufacturers_id

                                         LEFT JOIN specials s 
                                            ON p.products_id = s.products_id
                                         LEFT JOIN products_attributes pa
                                            ON p.products_id = pa.products_id
                                            LEFT JOIN products_options po
                                               ON pa.options_id = po.products_options_id
                                            LEFT JOIN products_options_values pov
                                               ON pa.options_values_id = pov.products_options_values_id,
                                   WHERE
                                          p.products_status = '1'
                                      AND (    pd.products_name LIKE '%a%'
                                            OR pd.products_description LIKE '%a%'
                                            OR po.products_options_name LIKE '%a%'
                                            OR pov.products_options_values_name LIKE '%a%' )
                                   ORDER BY
                                      p.products_id DESC) m 
                   WHERE
                          pa.products_id = m.products_id
                      AND pa.options_id = 1
                   GROUP BY
                      pov.products_options_values_id 
                   ORDER BY
                      pov.products_options_values_sort_order ASC) q
            ON ps.products_id = q.products_id 
   ORDER BY
      pav.products_options_values_sort_order ASC

您已加入您的类别表,但未提取任何值或其他条件来返回列。我因此删除了它,因为它是不必要的。随着那个消失,然后我查看了您的 Products_To_Categories 表,并且除了没有在其他任何地方使用并因此删除的额外连接之外,它也与查询无关。 Manufacturers 表也不是必需的,因为您得到的只是制造商的 ID,它存在于 products 表中,因此不需要 ANOTHER 表

现在谈谈索引。我会为您的表格查看以下索引

table                   index
products                ( products_status, products_id )
products_description    ( products_id, language_id )
products_to_categories  ( products_id, categories_id ) 
categories              ( categories_id )
manufacturers           ( manufacturers_id )
specials                ( products_id, status, specials_new_products_price )
products_attributes     ( products_id, options_id, options_values_id )
products_options        ( products_options_id, products_options_name )
products_options_values ( products_options_values_id, products_options_values_name )

这只是审核的第一次通过。现在我想采取一个额外的步骤来可能删除一层嵌套查询。您的 INNER-MOST 查询连接到“products_options_values”,但仅适用于语言 ID = 1。为什么不将该列条件添加到内部查询中。然后将 pov.products_options_values_id 和 pov.products_options_values_name 列添加到您的内部查询和语言 ID 到内部查询,您不需要再次重新加入它们。它们可以通过“m”引用。最内层查询的别名。 vs 重新加入。

同样,您的最内层查询对 products_attributes 执行 LEFT JOIN,但在“m”别名查询结果之外仅应用 options_id = 1 的 WHERE 子句。这没有意义。为什么不把它也添加到最里面的查询中。如果您只是在寻找诸如 Language ID = 1 或 Options_ID = 1 之类的东西,那么消除额外的级别可能会很有帮助,因为您会限制所有记录的拉取。

同样,此格式化查询与您的上下文相同,只是结构略有不同,缩进更短,以便更好地查看上下文和嵌套需求。

聊天反馈

select 
      PQ.*
   from
    ( SELECT STRAIGHT_JOIN DISTINCT 
          p.products_id, 
          p.products_image, 
          p.products_quantity, 
          p.products_status, 
          p.manufacturers_id, 
          p.products_date_added, 
          p.products_subimage1, 
          pd.products_name, 
          p.products_price, 
          p.products_length, 
          p.products_width, 
          p.products_height, 
          pov.products_options_values_id, 
          pov.products_options_values_name, 
          pov.language_id, 
          pov.products_options_values_sort_order, 
          p.products_tax_class_id,
          IF(s.status, s.specials_new_products_price, NULL) 
             AS specials_new_products_price, 
          IF(s.status, s.specials_new_products_price, p.products_price) 
             AS final_price, 
          IF(p.clearance_price < p.products_cost*2.25, p.clearance_price, p.products_cost*2.25) 
             AS sorting_price 
       FROM 
          products_attributes pa 
             JOIN products_options_values pov 
                ON pa.options_values_id = pov.products_options_values_id
                AND pov.language_id = '1' 
                LEFT JOIN products_options po 
                   ON pa.options_id = po.products_options_id 
             JOIN products p 
                ON pa.products_id = p.products_id
                AND p.products_status = '1' 
                JOIN products_description pd
                   ON p.products_id = pd.products_id 
                   AND pd.language_id = '1' 
                JOIN products_to_categories p2c 
                   ON p.products_id = p2c.products_id 
                   JOIN categories c
                      ON p2c.categories_id = c.categories_id 
                LEFT JOIN manufacturers m 
                   using (manufacturers_id) 
                LEFT JOIN specials s 
                   ON p.products_id = s.products_id 
       WHERE 
              pa.options_id = '1' 
          AND (  pd.products_name LIKE '%a%' 
              OR po.products_options_name LIKE '%a%' 
              OR pov.products_options_values_name LIKE '%a%' 
              OR pd.products_description LIKE '%a%'))  PQ
   order by 
      PQ.Products_id

另外,请注意,我删除了最后的“订购依据”子句

【讨论】:

  • 非常感谢您花时间和精力编写此答案。我尝试了重写的查询,但它的性能比我的慢一点。然后我开始添加一些您建议的附加索引,并根据您在索引部分下的第一段修改查询。这有助于将查询速度提高约 30%。它仍然很慢。主要问题实际上是 INNER-MOST 查询 m (pastebin.com/1cCWkCrJ),它本身花费的时间太长。这是解释:grabilla.com/06202-44284c84-8a8a-4dff-a037-3f875e640070.png我真的迷路了。建议?
  • @CreativeMind,再次,不理解为什么您在外部级别有一些标准没有应用于内部将是一个问题。例如:内部查询是某事物的所有事务,但外部查询只关心 2015 年 12 月的事务。如果要查找特定集合(例如 Options_ID = 1 和 Language_ID = 1),为什么要查询所有记录。在内部进行限制在这些表索引中使用附加列的大多数级别会有所帮助,但我并不真正理解您对超级嵌套的推理。另外,您是否删除了不必要的联接/表?
  • 是的,我确实了解上下文。你有没有机会看看我的 pastebin 和另一个 EXPLAIN?
  • 我们可以快速聊聊吗? chat.stackoverflow.com/rooms/102353/…
  • @CreativeMind,抱歉,跑题了……我现在在那个聊天室里。
猜你喜欢
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 2015-04-11
  • 1970-01-01
  • 2018-09-07
  • 2022-01-09
  • 1970-01-01
相关资源
最近更新 更多