我们希望通过以下查询将calibration_data的条目映射到校准数据。但是在我看来这个查询的持续时间太长了(> 24小时)。
有可能进行任何优化吗? 我们现在添加了根据需要测试更多索引,但它对持续时间没有任何影响。
[编辑]
硬件不应该是最大的瓶颈
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;
答案 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;
这就是我所想的全部......