MySQL查询慢不知道如何优化

时间:2019-11-13 18:05:53

标签: mysql performance mariadb mysql-slow-query-log

MySQL查询SLOW不知道如何优化

我认为我对60 GB RAM 10核SSD硬件没问题

您好,我在查询以下查询的Mysql上运行缓慢时遇到了一个大问题:

# Thread_id: 1165100  Schema: back-Alvo-11-07-19  QC_hit: No
# Query_time: 9.015205  Lock_time: 0.000188  Rows_sent: 1  Rows_examined: 2616880
# Rows_affected: 0
SET timestamp=1568549358;
SELECT count(*) as total_rows FROM(
(SELECT m.*
FROM phpfox_channel_video AS m
INNER JOIN phpfox_channel_category AS mc
    ON(mc.category_id = mc.category_id)
INNER JOIN phpfox_channel_category_data AS mcd
    ON(mcd.video_id = m.video_id)

WHERE m.in_process = 0 AND m.view_id = 0 
     AND m.module_id = 'videochannel' 
    AND m.item_id = 0 AND m.privacy IN(0) 
    AND mcd.category_id = 17
GROUP BY m.video_id
ORDER BY m.time_stamp DESC
LIMIT 12

)) AS m
JOIN phpfox_user AS u
    ON(u.user_id = m.user_id);

此查询运行非常缓慢,您可以看到9秒

在寻找优化查询的在线帮助时,总是谈论添加索引,

正如您在下面的解释说明中所看到的,我已经有索引

enter image description here

你们对我应该提高查询速度的想法有何想法?我不是DB家伙很难解决这个问题。这是一个网站,拥有40万个视频。

谢谢

3 个答案:

答案 0 :(得分:1)

该说明表明您未在表phpfox_channel_video as m上使用索引,并且正在表phpfox_channel_category AS mc上使用临时索引,这意味着它没有在使用索引,而是在构建一个首先进行索引,这需要花费大量时间。

此外,表phpfox_channel_category_data AS mcd的索引可能更好。

您需要的索引是:

CREATE INDEX idx_cat_data_video_id ON phpfox_channel_category_data
  (category_id, video_id);
CREATE INDEX idx_channel_cat_id ON phpfox_channel_category (category_id);
CREATE INDEX idx_video_mult ON phpfox_channel_video
  (in_process, view_id, module_id, item_id, privacy, video_id, time_stamp);

答案 1 :(得分:1)

  • 如果只打算m.*,请不要获取COUNT(*)
  • 如果phpfox_channel_category是多对多映射表,请按照http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table中的提示进行操作
  • m需要INDEX(in_process, view_id, module_id, item_id, privacy)的任何顺序。
  • 避免使用GROUP BY

             INNER JOIN  phpfox_channel_category AS mc ON(mc.category_id = mc.category_id)
             INNER JOIN  phpfox_channel_category_data AS mcd ON(mcd.video_id = m.video_id)
             AND  mcd.category_id = 17
             GROUP BY  m.video_id
    

->(类似)

    AND EXISTS(
                 SELECT 1
                     FROM phpfox_channel_category      AS mc
                     JOIN phpfox_channel_category_data AS mcd
                             ON mcd.video_id = mc.video_id
                     WHERE mcd.video_id = 17
                       AND mc.video_id = m.video_id
              )

答案 2 :(得分:0)

让我们确保我们正在优化正确的查询。我建议我们在ON子句中检查这种情况:

 mc.category_id = mc.category_id

我们知道mc中的每一行都将为TRUE,且其非空值为category_id。我们可以将该条件表示为:

 mc.category_id IS NOT NULL

这意味着联接几乎是交叉联接; m返回的每一行都与mc返回的每一行匹配。也就是说,我们可以得到等效的结果:

   FROM phpfox_channel_video    m
   JOIN phpfox_channel_category    mc
     ON mc.category_id IS NOT NULL

我怀疑这不是我们追求的结果。我认为我们的意思是匹配m.category_id。但这只是一个猜测。


如果video_id列是m上的PRIMARY KEY或UNIQUE KEY,则可以通过使用带有相关子查询的EXISTS来避免创建重复行的联接,从而避免潜在的昂贵GROUP BY操作。如果我们可以避免生成具有重复值video_id的中间结果,那么就可以避免需要执行GROUP BY

此外,对于内联视图查询,我们可以只返回所需的表达式,而不是返回所有列*。在外部查询中,唯一引用的列是user_id

所以我们可以这样写:

SELECT COUNT(*) AS total_rows
  FROM (
         SELECT m.user_id
           FROM phpfox_channel_video m
          WHERE EXISTS ( SELECT 1
                           FROM phpfox_channel_category mc
                          WHERE mc.category_id  = m.category_id
                      --        mc.category_id = mc.category_id  -- <original
                       )
            AND EXISTS ( SELECT 1
                           FROM phpfox_channel_category_data mcd
                          WHERE mcd.video_id     = m.video_id
                            AND mcd.category_id  = 17
                       )
            AND m.in_process  = 0
            AND m.view_id     = 0
            AND m.module_id   = 'videochannel'
            AND m.item_id     = 0
            AND m.privacy   IN (0)
          ORDER BY m.time_stamp DESC
          LIMIT 12
       ) d
    JOIN phpfox_user u
      ON u.user_id = d.user_id

为了进行调整,m的最佳索引将包含具有相等谓词的前导列,其后是time_stamp列,这样我们就可以避免执行“使用文件排序”操作,ORDER BY可以通过按索引顺序返回行来满足。看来我们需要对行进行排序的原因是为了LIMIT子句。

... ON phpfox_channel_video (in_process, view_id, item_id, module_id
          , time_stamp, video_id, ... )

另外两个表,我们希望索引的前导列具有相等谓词

... ON phpfox_channel_category_data (video_id, category_id, ...)

... ON phpfox_channel_category ( category_id, ... )

注意:

(尚不清楚为什么我们需要内联视图,并且我们延迟了user_id引用的联接。然后,整个查询的重点对我来说并不很明显;我是只是根据给定的SQL提供了对条件category_id的更改的重写。)

以上假设category_id中存在m列,并且它是一对多关系。

但是如果这不是真的...如果mcd表实际上是联结表,则解决了视频和类别之间的多对多关系,因此联接条件本来应该是

 mcd.category_id = mc.category_id 
   ^

然后,我们希望将上面查询中的WHERE EXISTSAND EXISTS替换为单个相关子查询。像这样:

SELECT COUNT(*) AS total_rows
  FROM (
         SELECT m.user_id
           FROM phpfox_channel_video m
          WHERE EXISTS ( SELECT 1
                           FROM phpfox_channel_category mc
                           JOIN phpfox_channel_category_data mcd
                             ON mcd.category_id  = mc.category_id 
                          WHERE mcd.video_id     = m.video_id
                            AND mcd.category_id  = 17
                       )
            AND m.in_process  = 0
            AND m.view_id     = 0
            AND m.module_id   = 'videochannel'
            AND m.item_id     = 0
            AND m.privacy   IN (0)
          ORDER BY m.time_stamp DESC
          LIMIT 12
       ) d
    JOIN phpfox_user u
      ON u.user_id = d.user_id