mysql - 改善缓慢的 Mysql 查询

标签 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)

事件表包含以下行数:

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 秒。重复 相同的查询会立即给出结果,但是如果我“刷新表”并且 第三次重复查询所用时间再次为 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)

使用 Intersects 需要 1 分 12 秒?

最佳答案

对于这个查询:

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'));

关于mysql - 改善缓慢的 Mysql 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33844488/

相关文章:

Javascript 加法给我一个多于 2 位小数的输出

mysql - 比较两个表并找到匹配的列

php - 使用CONVERT转换mysql中的特殊字符

Mysql 服务器的编码与客户端的编码不同(latin1 与 utf8mb4)。有多糟糕?

mysql - 是否可以减少重复的子查询查找?

mysql - NVME SSD 会加速繁重的查询吗?选择大偏移量/表扫描等?

java - @Transactional 上的 MySQL 套接字超时行为

php - 只会显示 mysql_fetch_array 的第一部分

mysql - 如何在 MySQL 的序列化器字段中查找值?

php - CodeIgniter 的喜欢和地点