SQL请求优化

时间:2015-05-21 07:39:40

标签: mysql sql optimization

我有一个SQL请求,它在工作时占用了我100%的VM CPU。我想知道如何优化它:

SELECT g.name AS hostgroup
     , h.name AS hostname
     , a.host_id
     , s.display_name AS servicename
     , a.service_id
     , a.entry_time AS ack_time
     , (  SELECT ctime 
          FROM logs 
          WHERE logs.host_id = a.host_id 
          AND logs.service_id = a.service_id 
          AND logs.ctime < a.entry_time 
          AND logs.status IN (1, 2, 3) 
          AND logs.type = 1 
          ORDER BY logs.log_id DESC 
          LIMIT 1) AS start_time
     , ar.acl_res_name AS timeperiod
     , a.state AS state
     , a.author
     , a.acknowledgement_id AS ack_id
FROM centstorage.acknowledgements a
LEFT JOIN centstorage.hosts h ON a.host_id = h.host_id
LEFT JOIN centstorage.services s ON a.service_id = s.service_id
LEFT JOIN centstorage.hosts_hostgroups p ON a.host_id = p.host_id
LEFT JOIN centstorage.hostgroups g ON  g.hostgroup_id = p.hostgroup_id
LEFT JOIN centreon.hostgroup_relation hg ON a.host_id = hg.host_host_id
LEFT JOIN centreon.acl_resources_hg_relations hh ON hg.hostgroup_hg_id = hh.hg_hg_id
LEFT JOIN centreon.acl_resources ar ON hh.acl_res_id = ar.acl_res_id
WHERE ar.acl_res_name != 'All Resources'
AND YEAR(FROM_UNIXTIME( a.entry_time )) = YEAR(CURDATE())
AND MONTH(FROM_UNIXTIME( a.entry_time )) = MONTH(CURDATE())
AND a.service_id is not null
ORDER BY a.acknowledgement_id ASC

问题出在这一部分:

(SELECT ctime FROM logs
 WHERE logs.host_id = a.host_id
   AND logs.service_id = a.service_id
   AND logs.ctime < a.entry_time
   AND logs.status IN (1, 2, 3)
   AND logs.type = 1
 ORDER BY logs.log_id DESC
 LIMIT 1) AS start_time

表日志非常庞大,有些朋友告诉我使用缓冲表/数据库,但我很清楚这些事情,我不知道该怎么做。

查询有一个EXPLAIN EXTENDED:Here !

他似乎只会检查2行表日志,为什么需要这么多时间呢? (表日志中有560000行)。

以下是这些表的所有索引:

centstorage.acknowledgements:

enter image description here centstorage.hosts:

enter image description here centstorage.services:

enter image description here centstorage.hosts_hostgroups:

enter image description here centstorage.hostgroups:

enter image description here centreon.hostgroup_relation:

enter image description here centreon.acl_resources_hg_relations:

enter image description here centreon.acl_resources:

enter image description here

5 个答案:

答案 0 :(得分:0)

对于SQL Server,可以使用MAXDOP

定义查询的最大并行度

例如,您可以在查询结尾处定义

option (maxdop 2) 

我非常确定MySql中的等价物。

如果执行时间不相关,您可以尝试接近这种情况。

答案 1 :(得分:0)

  1. 创建一个临时表,其中确认条件,模式将在最终结果中包含所需的列,并在JOIN中与所有7个表一起使用

    CREATE TEMPORARY TABLE __tempacknowledgements AS SELECT g.name AS hostgroup
         , '' AS hostname
         , a.host_id
         , s.display_name AS servicename
         , a.service_id
         , a.entry_time AS ack_time
         , '' AS AS start_time
         , '' AS timeperiod
         , a.state AS state
         , a.author
         , a.acknowledgement_id AS ack_id
    FROM centstorage.acknowledgements a
    WHERE YEAR(FROM_UNIXTIME( a.entry_time )) = YEAR(CURDATE())
    AND MONTH(FROM_UNIXTIME( a.entry_time )) = MONTH(CURDATE())
    AND a.service_id IS NOT NULL
    ORDER BY a.acknowledgement_id ASC;
    
  2. 或使用正确的列定义创建

    1. 更新左边连接的所有表中的字段,可以在更新中使用内部连接。您应该编写7个不同的更新语句。下面给出了两个例子。

      UPDATE __tempacknowledgements a JOIN centstorage.hosts h USING(host_id)
      SET a.name=h.name;
      
      UPDATE __tempacknowledgements s JOIN centstorage.services h USING(service_id)
      SET a.acl_res_name=s.acl_res_name;
      
    2. 类似地使用Join with Logs从日志更新ctime,这是第8次更新声明。

    3. 从临时表中选择选择。
    4. drop temp table
    5. 可以为此写一个sp。

答案 2 :(得分:0)

LEFT JOIN转为JOIN,除非您确实需要LEFT

AND YEAR(FROM_UNIXTIME( a.entry_time )) = YEAR(CURDATE())
AND MONTH(FROM_UNIXTIME( a.entry_time )) = MONTH(CURDATE())
AND a.service_id is not null

你有a.service_id is not null的任何行吗?如果没有,请摆脱它。

如前所述,该日期比较不会优化。以下是使用的内容:

AND a.entry_time >= CONCAT(LEFT(CURDATE(), 7), '-01')
AND a.entry_time <  CONCAT(LEFT(CURDATE(), 7), '-01') + INTERVAL 1 MONTH

添加其中一个(取决于我上面的评论):

INDEX(entry_time)
INDEX(service_id, entry_time)

相关子查询很难优化。此索引(在logs上)可能有所帮助:

INDEX(type, host_id, service_id, status)

答案 3 :(得分:0)

在哪里是时间杀手! 代替 logs.status IN(1,2,3) 使用 logs.status = 1或logs.status = 2或logs.status = 3

答案 4 :(得分:0)

我已经轻松地重新格式化了我的可读性参考查询,并且更好地查看了表之间的关系......否则忽略该部分。

SELECT 
      g.name AS hostgroup, 
      h.name AS hostname, 
      a.host_id, 
      s.display_name AS servicename, 
      a.service_id, 
      a.entry_time AS ack_time,
      ( SELECT 
              ctime 
           FROM 
              logs 
           WHERE 
                  logs.host_id = a.host_id 
              AND logs.service_id = a.service_id 
              AND logs.ctime < a.entry_time 
              AND logs.status IN (1, 2, 3) 
              AND logs.type = 1 
           ORDER BY 
              logs.log_id DESC 
           LIMIT 1) AS start_time, 
      ar.acl_res_name AS timeperiod, 
      a.state AS state, 
      a.author, 
      a.acknowledgement_id AS ack_id
   FROM 
      centstorage.acknowledgements a
         LEFT JOIN centstorage.hosts h 
            ON a.host_id = h.host_id
         LEFT JOIN centstorage.services s 
            ON a.service_id = s.service_id
         LEFT JOIN centstorage.hosts_hostgroups p 
            ON a.host_id = p.host_id
            LEFT JOIN centstorage.hostgroups g 
               ON p.hostgroup_id = g.hostgroup_id 
         LEFT JOIN centreon.hostgroup_relation hg 
            ON a.host_id = hg.host_host_id
            LEFT JOIN centreon.acl_resources_hg_relations hh 
               ON hg.hostgroup_hg_id = hh.hg_hg_id
               LEFT JOIN centreon.acl_resources ar 
                  ON hh.acl_res_id = ar.acl_res_id
   WHERE 
          ar.acl_res_name != 'All Resources'
      AND YEAR(FROM_UNIXTIME( a.entry_time )) = YEAR(CURDATE())
      AND MONTH(FROM_UNIXTIME( a.entry_time )) = MONTH(CURDATE())
      AND a.service_id is not null
   ORDER BY 
      a.acknowledgement_id ASC

我首先建议您使用&#34;致谢&#34;表并且索引至少为(entry_time,acknowledgement_id)。接下来,更新WHERE子句。因为您正在运行一个函数来将unix时间戳转换为日期并分别抓住YEAR(和月份),所以我不相信它正在利用索引,因为它必须为每一行计算。为了提升它,unix时间戳只是一个代表特定时间点的秒的数字。如果您要查找特定月份,则预先计算开始和结束的unix时间并运行该范围。有点像...

和a.entry_time&gt; = UNIX_TIMESTAMP(&#39; 2015-10-01&#39;) 和a.entry_time&lt; UNIX_TIMESTAMP(&#39; 2015-11-01&#39;)

这样,它可以计算10月31日11月1日11:59:59之间的所有秒数。

然后,没有我的眼镜可以更清楚地看到所有图像,而且今天早上的时间很短,我会确保每张桌子上至少有以下索引

table              index
logs               ( host_id, service_id, type, status, ctime, log_id )
acknowledgements   ( entry_time, acknowledgement_id, host_id, service_id )
hosts              ( host_id, name )
services           ( service_id, display_name )
hosts_hostgroups   ( host_id, hostgroup_id )
hostgroups         ( hostgroup_id, name )
hostgroup_relation ( host_host_id, hostgroup_hg_id )
acl_resources_hg_relations ( hh_hg_id, acl_res_id )
acl_resources ar   ( acl_res_id, acl_res_name )

最后,您的相关子查询字段将成为每个行处理的杀手,但希望其他索引优化提示有助于提高性能。