优化Mysql查询左连接

时间:2016-11-21 09:24:00

标签: mysql performance left-join query-optimization explain

我们希望通过以下查询将calibration_data的条目映射到校准数据。但是在我看来这个查询的持续时间太长了(> 24小时)。

有可能进行任何优化吗? 我们现在添加了根据需要测试更多索引,但它对持续时间没有任何影响。

[编辑]

硬件不应该是最大的瓶颈

  • 128 GB RAM
  • 1TB SSD RAID 5
  • 32核

EXPLAIN结果

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                                          |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+
|  1 | SIMPLE      | cal   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2009 |   100.00 | Using temporary; Using filesort                |
|  1 | SIMPLE      | m     | NULL       | ALL  | visit         | NULL | NULL    | NULL | 3082466 |   100.00 | Range checked for each record (index map: 0x1) |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+

查询耗时太长:

Insert into knn_data (SELECT cal.X           AS X, 
        cal.Y           AS Y, 
        cal.BeginTime   AS BeginTime, 
        cal.EndTime     AS EndTime, 
        avg(m.dbm_ant)  AS avg_dbm_ant, 
        m.ant_id        AS ant_id, 
        avg(m.location) avg_location, 
        count(*)        AS count, 
        m.visit 
 FROM   calibration cal 
        LEFT join calibration_data m
          ON m.visit BETWEEN cal.BeginTime AND cal.EndTime 
 GROUP  BY cal.X, 
           cal.Y, 
           cal.BeginTime, 
           cal. BeaconId, 
           m.ant_id,
           m.macHash,
           m.visit; 

表knn_data:

    CREATE TABLE `knn_data` (
  `X` int(11) NOT NULL,
  `Y` int(11) NOT NULL,
  `BeginTime` datetime NOT NULL,
  `EndTIme` datetime NOT NULL,
  `avg_dbm_ant` float DEFAULT NULL,
  `ant_id` int(11) NOT NULL,
  `avg_location` float DEFAULT NULL,
  `count` int(11) DEFAULT NULL,
  `visit` datetime NOT NULL,
  PRIMARY KEY (`ant_id`,`visit`,`X`,`Y`,`BeginTime`,`EndTIme`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表校准

BeaconId, X, Y, BeginTime, EndTime
41791, 1698, 3944, 2016-11-12 22:44:00, 2016-11-12 22:49:00


CREATE TABLE `calibration` (
  `BeaconId` int(11) DEFAULT NULL,
  `X` int(11) DEFAULT NULL,
  `Y` int(11) DEFAULT NULL,
  `BeginTime` datetime DEFAULT NULL,
  `EndTime` datetime DEFAULT NULL,
  KEY `x,y` (`X`,`Y`),
  KEY `x` (`X`),
  KEY `y` (`Y`),
  KEY `BID` (`BeaconId`),
  KEY `beginTime` (`BeginTime`),
  KEY `x,y,beg,bid` (`X`,`Y`,`BeginTime`,`BeaconId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表校准_数据

macHash, visit, dbm_ant, ant_id, mac, isRand, posX, posY, sources, ip, dayOfMonth, location, am, ar
'f5:dc:7d:73:2d:e9', '2016-11-12 22:44:00', '-87', '381', 'f5:dc:7d:73:2d:e9', NULL, NULL, NULL, NULL, NULL, '12', '18.077636300207715', 'inradius_41791', NULL


CREATE TABLE `calibration_data` (
  `macHash` varchar(100) COLLATE utf8_bin NOT NULL,
  `visit` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `dbm_ant` int(3) NOT NULL,
  `ant_id` int(11) NOT NULL,
  `mac` char(17) COLLATE utf8_bin DEFAULT NULL,
  `isRand` tinyint(4) DEFAULT NULL,
  `posX` double DEFAULT NULL,
  `posY` double DEFAULT NULL,
  `sources` int(2) DEFAULT NULL,
  `ip` int(10) unsigned DEFAULT NULL,
  `dayOfMonth` int(11) DEFAULT NULL,
  `location` varchar(80) COLLATE utf8_bin DEFAULT NULL,
  `am` varchar(300) COLLATE utf8_bin DEFAULT NULL,
  `ar` varchar(300) COLLATE utf8_bin DEFAULT NULL,
  KEY `visit` (`visit`),
  KEY `macHash` (`macHash`),
  KEY `ant, time` (`dbm_ant`,`visit`),
  KEY `beacon` (`am`),
  KEY `ant_id` (`ant_id`),
  KEY `ant,mH,visit` (`ant_id`,`macHash`,`visit`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

2 个答案:

答案 0 :(得分:1)

一次性任务?那没关系?加载此数据后,您将逐步更新"摘要表"每一天?

收缩数据类型 - 庞大的数据需要更长的时间来处理。示例:4字节INT DayOfMonth可以是1字节TINYINT UNSIGNED

您正在将TIMESTAMP移动到DATETIME。这可能会或可能不会像您期望的那样发挥作用。

INT UNSIGNED适用于IPv4,但您无法在其中使用IPv6。

COUNT(*)可能不需要4字节INT;看到较小的变种。

在适当情况下使用UNSIGNED

mac-address占用19个字节;它可以很容易地转换为6字节BINARY(6)。请参阅REPLACE()UNHEX()HEX()

innodb_buffer_pool_size的设置是什么?对于你拥有的大RAM,它可能大约是100G。

时间范围是否重叠?如果没有,请利用这一点。另外,请勿在{{1​​}}中添加不必要的列,例如PRIMARY KEY

EndTime列的顺序与knn_data的GROUP BY相同;这样可以在PRIMARY KEY期间避免大量的块分割。

最大的问题是INSERT中没有有用的索引,因此calibration_data必须一次又一次地进行全表扫描! 3M排的2K扫描结果!让我关注这个问题......

没有好办法JOIN,因为MySQL不知道日期时间范围是否重叠。在这种情况下,没有真正的解决办法,所以让我以不同的方式处理......

开始和结束'定期'?喜欢每个小时?因此,我们可以对WHERE x BETWEEN start AND end进行某种计算而不是。如果是这种情况,请告诉我。我会继续思考。

答案 1 :(得分:-1)

这是一个令人讨厌且经典的一个"范围"查询:优化器不使用索引,最终进行全表扫描。在您的解释计划中,您可以在专栏type=ALL上看到这一点。

理想情况下,您应该在关键列

中使用type=range和其他内容

一些想法:

我怀疑从

改变你的联合
ON m.visit BETWEEN cal.BeginTime AND cal.EndTime 

ON m.visit >= cal.BeginTime AND m.visit <= cal.EndTime

会起作用,但还是试一试。

在两个表上触发ANALYSE TABLE。这将更新表上的统计信息,可能有助于优化者做出正确的决定(即使用索引)

将查询更改为此可能也有助于强制优化器使用索引:

Insert into knn_data (SELECT cal.X           AS X, 
        cal.Y           AS Y, 
        cal.BeginTime   AS BeginTime, 
        cal.EndTime     AS EndTime, 
        avg(m.dbm_ant)  AS avg_dbm_ant, 
        m.ant_id        AS ant_id, 
        avg(m.location) avg_location, 
        count(*)        AS count, 
        m.visit 
 FROM   calibration cal 
        LEFT join calibration_data m
          ON m.visit >= cal.BeginTime 
 WHERE m.visit <= cal.EndTime 
 GROUP  BY cal.X, 
           cal.Y, 
           cal.BeginTime, 
           cal. BeaconId, 
           m.ant_id,
           m.macHash,
           m.visit; 

这就是我所想的全部......