2016-11-21 2 views
1

Мы хотим сопоставить записи калибровочных данных с данными калибровки по следующему запросу. Но продолжительность этого запроса слишком длинная, по моему мнению (> 24 часа).Оптимизация Mysql Query Left Join

Возможна ли оптимизация? Мы добавили для тестирования больше индексов по мере необходимости прямо сейчас, но это не повлияло на продолжительность.

[Редактировать]

Аппаратное обеспечение не должно быть самым большим узким

  • 128 GB RAM
  • 1 ТБ SSD RAID-5
  • 32 ядер

РАЗЪЯСНЯЕМ результат

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+ 
| 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; 

Таблица calibration_data

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

> 24h !!!!!!!!!!! – Strawberry

+0

@Felix есть ли какое-либо совпадение между временем калибровки или интервалами? Таким образом, каждая строка в 'calibration_data' относится к одной калибровке? Вы, вероятно, можете сократить время выполнения, а затем много (примерно 30 минут с вашим количеством бара). Изменены ли значения времени для существующих строк или вы просто вставляете новые строки? Затем вы можете спуститься до 1 минуты. И еще один вопрос: является ли «BeaconId» первичным ключом для вашей таблицы «калибровка», или может ли это быть a) «null» и «b») несколько записей с тем же beaconId? – Solarflare

+0

@Solarflare это как раз и главная проблема. Каждая строка в параметрах calibration_data может принадлежать нескольким или нескольким точкам калибровки. Таблицы являются историческими данными, поэтому никаких наступающих изменений нет. BeaconId не может быть первичным ключом, потому что они не уникальны. Но намек Томаса сократил время вычисления примерно до 3 часов, что лучше, чем эпические длинные запросы. Но большое спасибо за вашу помощь. – Felix

ответ

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

Это все, о чем я думаю ...

+0

Спасибо! Попытка вашего решения с условием включения и использования! Объяснение показывает фильтр из 11.11, поэтому стоит проверить – Felix

+0

Нет 'BETWEEN' идентичен'> = 'и т. Д. Обратите внимание на потенциальную ошибку: обе формулировки _inclusive_, поэтому элементы на границах могут быть включены в два разных ведра! –

+0

Нет для 'ON'. Поскольку он менее ограничительный, он будет сканировать дополнительные строки. –

1

Неповторимая задача? Тогда это не имеет значения? После загрузки этих данных вы будете поэтапно обновлять «сводную таблицу» каждый день?

Термоусаживаемые типы данных - объемные данные требуют больше времени для обработки. Пример: 4-байтовый INTDayOfMonth может быть 1 байт TINYINT UNSIGNED.

Вы перемещаете TIMESTAMP в DATETIME. Это может работать или не работать так, как вы ожидаете.

INT UNSIGNED подходит для IPv4, но вы не можете вместить в него IPv6.

COUNT(*), возможно, не требуется 4-байтовый INT; см. меньшие варианты.

Используйте, пожалуйста, UNSIGNED.

MAC-адрес занимает 19 байтов, как у вас есть; его можно легко преобразовать в/из 6-байтового BINARY(6). См. REPLACE(), UNHEX(), HEX() и т. Д.

Что такое настройка innodb_buffer_pool_size? Это может быть около 100G для большой RAM, которую вы имеете.

Удлиняют ли интервалы времени? Если нет, воспользуйтесь этим. Также не включайте ненужные столбцы в PRIMARY KEY, например EndTime.

Имейте колонки GROUP BY в том же порядке, что и PRIMARY KEY of knn_data; это позволит избежать большого количества разбиений блоков во время INSERT.

Большая проблема заключается в том, что в calibration_data нет полезного индекса, поэтому JOIN должен выполнять полное сканирование таблицы снова и снова! Обрезанные 2K сканирования 3M строк! Позвольте мне сфокусироваться на этой проблеме ...

Нет никакого способа сделать WHERE x BETWEEN start AND end, потому что MySQL не знает, перекрываются ли диапазоны datetime. В этом контексте нет реального лечения, поэтому позвольте мне подойти по-другому ...

Начальные и конечные «обычные»? Как каждый час? Таким образом, мы можем сделать какие-то вычисления вместоBETWEEN. Дайте мне знать, если это так; Я продолжу свои мысли.

+0

Привет, Рик! В этой форме это одно время, но это имело огромное влияние на изучение производительности до сих пор! Таким образом, ваш ответ очень полезен для будущих улучшений! Я проведу ваши рекомендуемые улучшения позже с меньшими типами! - Включение между известными! - Наш innodb_buffer находится в 104 ГБ - Таймеры не являются регулярными, они отличаются в 4-18 минут Так что спасибо за ввод! Best Felix – Felix

+0

Я рад, что вы видите это как опыт обучения. 104G - хорошо; возможно, все данные остались кэшированными; поэтому было> 24 часа одного ядра процессора. (Извините, не существует параллелизма.) Нерегулярные диапазоны времени - я мог бы разработать сложную схему, но не хочу (мой мозг будет больно). –

+0

(я был бы рад принять «принятое решение» из другого ответа.) –