[英]Optimization Mysql Query Left Join
我们希望通过以下查询将calibration_data的条目映射到校准数据。 但是在我看来这个查询的持续时间太长了(> 24h)。
有可能进行任何优化吗? 我们现在添加了根据需要测试更多索引,但它对持续时间没有任何影响。
[编辑]
硬件不应该是最大的瓶颈
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;
一次性任务? 那没关系? 获取此数据后,您是否会每天逐步更新“汇总表”?
收缩数据类型 - 庞大的数据需要更长的时间来处理。 示例:4字节的INT
DayOfMonth
可以是1字节的TINYINT UNSIGNED
。
您正在将TIMESTAMP
移动到DATETIME
。 这可能会或可能不会如您所愿。
对于IPv4, INT UNSIGNED
可以正常使用,但您无法在其中使用IPv6。
COUNT(*)
可能不需要4字节的INT
; 看到较小的变种。
在适当的地方使用UNSIGNED
。
mac-address以你拥有的方式占用19个字节; 它可以很容易地转换为6字节的BINARY(6)
。 参见REPLACE()
, UNHEX()
, HEX()
等。
innodb_buffer_pool_size
的设置是innodb_buffer_pool_size
? 你拥有的大RAM可能大约100G。
时间范围重叠吗? 如果没有,请利用这一点。 另外,不要在PRIMARY KEY
包含不必要的列,例如EndTime
。
使GROUP BY
列的顺序与knn_data的PRIMARY KEY
相同; 这样可以避免在INSERT
期间发生大量的块分裂。
最大的问题是在calibration_data
没有有用的索引,因此JOIN
必须一次又一次地进行全表扫描! 3M排的2K扫描结果! 让我关注这个问题......
没有好办法做WHERE x BETWEEN start AND end
因为MySQL不知道日期时间范围是否重叠。 在这种情况下,没有真正的解决办法,所以让我以不同的方式处理它......
开始和结束是“常规”吗? 喜欢每个小时? 因此,我们可以做一些计算而不是 BETWEEN
。 如果是这种情况,请告诉我。 我会继续思考。
对于“范围”查询来说,这是一个令人讨厌且经典的问题:优化器不会使用您的索引并最终进行全表扫描。 在您的解释计划中,您可以在列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;
这就是我所想的一切......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.