改进慢速Mysql查询

时间:2015-11-21 14:31:38

标签: mysql

我有一个包含事件的数据库表。

mysql> describe events;
+-------------+------------------+------+-----+---------------------+----------------+
| Field       | Type             | Null | Key | Default             | Extra          |
+-------------+------------------+------+-----+---------------------+----------------+
| device      | varchar(32)      | YES  | MUL | NULL                |                |
| psu         | varchar(32)      | YES  | MUL | NULL                |                |
| event       | varchar(32)      | YES  | MUL | NULL                |                |
| down_time   | timestamp        | NO   | MUL | CURRENT_TIMESTAMP   |                |
| up_time     | timestamp        | NO   | MUL | 0000-00-00 00:00:00 |                |
| id          | int(10) unsigned | NO   | PRI | NULL                | auto_increment |
+-------------+------------------+------+-----+---------------------+----------------+
6 rows in set (0.01 sec)

我想查找时间重叠的事件并使用以下查询:

SELECT *

FROM link_events a 
JOIN link_events b 

ON  ( a.down_time <= b.up_time )
AND ( a.up_time >= b.down_time )

WHERE (a.device = 'd1' AND b.device = 'd2')
AND   (a.psu = 'p1' AND b.psu = 'p2')
AND   (a.event = 'e1' AND b.event = 'e2');

+-------------+-----------+------------+---------------------+---------------------+--------+-------------+-----------+------------+---------------------+---------------------+--------+
| device      | psu       | event      | down_time           | up_time             | id     | device      | psu       | event      | down_time           | up_time             | id     |
+-------------+-----------+------------+---------------------+---------------------+--------+-------------+-----------+------------+---------------------+---------------------+--------+
| d1          | p1        | e1         | 2013-01-14 16:42:10 | 2013-01-14 16:43:00 | 374529 | d2          | p2        | e2         | 2013-01-14 16:42:14 | 2013-01-14 16:42:18 | 211570 |
| d1          | p1        | e1         | 2013-05-29 18:49:26 | 2013-05-30 12:31:15 | 374569 | d2          | p2        | e2         | 2013-05-30 08:48:20 | 2013-05-30 08:48:27 | 211787 |
| d1          | p1        | e1         | 2013-05-29 18:49:26 | 2013-05-30 12:31:15 | 374569 | d2          | p2        | e2         | 2013-05-30 08:48:54 | 2013-05-30 08:48:58 | 211788 |
+-------------+-----------+------------+---------------------+---------------------+--------+-------------+-----------+------------+---------------------+---------------------+--------+
3 rows in set (35.88 sec)

events表包含以下行数:

mysql> select count(*) from events;
+----------+
| count(*) |
+----------+
|   977759 |
+----------+
1 row in set (0.01 sec)

mysql> select count(*) from events where device = 'd1' and psu = 'p1' and event = 'e1';
+----------+
| count(*) |
+----------+
|    11397 |
+----------+
1 row in set (0.12 sec)

mysql> select count(*) from events where device = 'd2' and psu = 'p2' and event = 'e2';
+----------+
| count(*) |
+----------+
|      243 |
+----------+
1 row in set (0.00 sec)

数据库安装在Windows 7笔记本电脑上并使用MyISAM引擎。 有没有办法更好地组织数据库或更改索引 改善第一次查询的查询时间为35秒。重复 相同的查询给出了立即的结果,但是如果我冲洗表格&#39;和 第三次重复查询所花费的时间再次为35秒。 任何帮助表示赞赏!

以下是ADD KEY后的EXPLAIN输出:

mysql> EXPLAIN
    -> SELECT *
    ->
    -> FROM link_events a
    -> JOIN link_events b
    ->
    -> ON       ( a.down_time <= b.up_time )
    -> AND      ( a.up_time >= b.down_time )
    ->
    -> WHERE (a.device = 'd1' AND b.device = 'd2')
    -> AND (a.psu = 'l1' AND b.psu = 'l2')
    -> AND (a.event = 'e1' AND b.event = 'e2');
+----+-------------+-------+------+--------------------------------------------------------------------------------+---------------+---------+-------------------+------+-----------------------+
| id | select_type | table | type | possible_keys                                                                  | key           | key_len | ref               | rows | Extra                 |
+----+-------------+-------+------+--------------------------------------------------------------------------------+---------------+---------+-------------------+------+-----------------------+
|  1 | SIMPLE      | b     | ref  | device,psu,event,down_time,up_time,device_2,device_3                           | device_2      | 297     | const,const,const |  180 | Using index condition |
|  1 | SIMPLE      | a     | ref  | device,psu,event,down_time,up_time,device_2,device_3                           | device_2      | 297     | const,const,const | 7744 | Using index condition |
+----+-------------+-------+------+--------------------------------------------------------------------------------+---------------+---------+-------------------+------+-----------------------+
2 rows in set (0.07 sec)

新专栏:

mysql> describe link_events;
    +-------------+------------------+------+-----+---------------------+-----------------------------+
    | Field       | Type             | Null | Key | Default             | Extra                       |
    +-------------+------------------+------+-----+---------------------+-----------------------------+
    | device_name | varchar(32)      | YES  | MUL | NULL                |                             |
    | link_name   | varchar(32)      | YES  | MUL | NULL                |                             |
    | event_type  | varchar(32)      | YES  | MUL | NULL                |                             |
    | down_time   | timestamp        | NO   | MUL | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
    | up_time     | timestamp        | NO   | MUL | 0000-00-00 00:00:00 |                             |
    | span        | geometry         | NO   | MUL | NULL                |                             |
    | id          | int(10) unsigned | NO   | PRI | NULL                | auto_increment              |
    +-------------+------------------+------+-----+---------------------+-----------------------------+
    7 rows in set (0.03 sec)

说明:

mysql> EXPLAIN
    ->
    ->  SELECT
    ->
    ->  CONCAT('Link1','-', 'Link2') overlaps,
    ->  GREATEST(a.down_time,b.down_time) AS downtime,
    ->  LEAST(a.up_time,b.up_time) AS uptime,
    ->  TIME_TO_SEC(TIMEDIFF( LEAST(a.up_time,b.up_time),
    ->          GREATEST(a.down_time,b.down_time))) AS duration
    ->
    ->  FROM link_events a
    ->  JOIN link_events b
    ->
    ->  ON      Intersects (a.span, b.span)
    ->
    ->  WHERE (a.device_name = 'd1' AND b.device_name = 'd2')
    ->  AND (a.link_name = 'l1' AND b.link_name = 'l2')
    ->  AND (a.event_type = 'e1' AND b.event_type = 'e1');
    +----+-------------+-------+------+-------------------------------------------------------------------+---------------+---------+-------------------+-------+------------------------------------+
    | id | select_type | table | type | possible_keys                                                     | key           | key_len | ref               | rows  | Extra                              |
    +----+-------------+-------+------+-------------------------------------------------------------------+---------------+---------+-------------------+-------+------------------------------------+
    |  1 | SIMPLE      | a     | ref  | span,device_name,link_name,event_type,device_name_2,device_name_3 | device_name_2 | 297     | const,const,const |   383 | Using index condition              |
    |  1 | SIMPLE      | b     | ref  | span,device_name,link_name,event_type,device_name_2,device_name_3 | device_name_2 | 297     | const,const,const | 14580 | Using index condition; Using where |
    +----+-------------+-------+------+-------------------------------------------------------------------+---------------+---------+-------------------+-------+------------------------------------+
    2 rows in set (0.09 sec)

使用相交需要1分12秒?

2 个答案:

答案 0 :(得分:4)

对于此查询:

SELECT *
FROM link_events a JOIN
     link_events b 
     ON  (a.down_time <= b.up_time) AND (a.up_time >= b.down_time)
WHERE (a.device = 'd1' AND b.device = 'd2') AND
      (a.psu = 'p1' AND b.psu = 'p2') AND
      (a.event = 'e1' AND b.event = 'e2');

您需要link_events(device, psu, event, up_time, down_time)上的索引。为清楚起见,我会更像这样表达查询:

SELECT *
FROM link_events a JOIN
     link_events b 
     ON  (a.down_time <= b.up_time) AND (a.up_time >= b.down_time)
WHERE (a.device, a.psu, a.event) IN (('d1', 'p1', 'e1')) AND
      (b.device, a.psu, a.event) IN (('d2', 'p2', 'e2'));

答案 1 :(得分:0)

尝试:

ALTER TABLE link_events ADD KEY(device,psu,event,up_time),
  ADD KEY(device,psu,event,down_time)

希望这足够有选择性。如果这没有帮助,请发布EXPLAIN的结果,这样我们就可以确保优化器尽力而为,如果需要,我们将从那里开始。

编辑:

重要的是要理解并非所有索引对于特定查询都具有相同的值。一个常见的错误是将索引视为一个魔术工作者,如果您只引用索引中的列,它将自动加速查询。情况并非如此。需要设计密钥,并且需要以允许记录的最佳访问路径的方式编写查询。更改可能显得无关紧要的内容(例如索引中列的顺序或编写SQRT(x) = 4.4而不是x = 4.4 * 4.4)可能会使索引无法使用,并使查询速度降低一千甚至一百万或更多。

我强烈建议您阅读:

http://dev.mysql.com/doc/refman/5.7/en/mysql-indexes.html

了解MySQL如何使用密钥可以在将来为您节省很多麻烦。

编辑2 - 另一个想法是添加一个包含span GEOMETRY NOT NULL, SPATIAL KEY (span)的列linestring(point(up_time,0),point(down_time,0)) - 时间需要是数字(例如,您可以使用UNIX_TIMESTAMP()转换) - 并使用{{1在查询中。通过一些微调,这有可能比改进的查询快得多,因为使用专门为此类事物设计的基于几何的算法来检测跨度交叉。