Как исправить медленный запрос на обновление - PullRequest
0 голосов
/ 10 января 2019

Я использую следующий запрос для заполнения таблицы дневных экстремальных температур из большого набора показаний. Есть 33 термометра, и показания взяты каждую минуту; таким образом, существует около 46K показаний (то есть строк) в день. Но только 33 строки добавляются к extremes каждый день.

Изначально я предполагал, что этот запрос будет выполняться каждый раз, когда добавляется новое чтение, чтобы поддерживать актуальность сегодняшних крайностей. Однако вскоре я обнаружил, что этот запрос занимает ДЛИННОЕ время: на моем MacBook 5,5 минут при чтении за полный день.

Мне было бы очень интересно узнать, почему это так медленно, и, возможно, как сделать этот запрос быстрее, или лучшей альтернативой. Примечание. extremes имеет как Sensor_ID, так и Date в качестве первичных ключей, поскольку это то, что уникально в каждой строке.

СПАСИБО !!

insert into extremes(Date, Sensor_ID, `min`, `max`, `avg`)
    select date(DateTime) as `Date`, Sensor_ID as Sensor_ID,
        min(Value) as `min`, max(Value) as `max`, avg(Value) as `avg`
        from readings where date(`DateTime`) = date(NOW())
    group by date(DateTime), Sensor_ID
on duplicate key update 
    `min` = values(`min`), `max` = values(`max`), `avg` = values(`avg`);

По запросу, вот таблицы

CREATE TABLE `readings` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Sensor_ID` int(11) NOT NULL,
  `DateTime` datetime NOT NULL,
  `Value` double NOT NULL,
  PRIMARY KEY (`ID`),
  UNIQUE KEY `ID_UNIQUE` (`ID`),
  KEY `ID_idx` (`Sensor_ID`),
  CONSTRAINT `ID` FOREIGN KEY (`Sensor_ID`) REFERENCES `sensors` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=54500039 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `extremes` (
  `Date` datetime NOT NULL,
  `Sensor_ID` int(11) NOT NULL,
  `min` double DEFAULT NULL,
  `max` double DEFAULT NULL,
  `avg` double DEFAULT NULL,
  `updates` int(11) DEFAULT '0',
  PRIMARY KEY (`Date`,`Sensor_ID`),
  KEY `ID_idx` (`Sensor_ID`),
  CONSTRAINT `foo` FOREIGN KEY (`Sensor_ID`) REFERENCES `sensors` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

Ответы [ 2 ]

0 голосов
/ 10 января 2019
UNIQUE KEY `ID_UNIQUE` (`ID`),

замедляет модификации до readings. Это избыточный , поскольку `PRIMARY KEY является уникальным ключом. Брось.

Делайте IODKU только на одну вставляемую строку, а не на все строки:

insert into extremes(Date, Sensor_ID, `min`, `max`)
    VALUES(... , ..., ..., ...)   -- Place constants here (from the sensor)
on duplicate key update 
    `min` = LEAST(`min`, values(`min`)),
    `max` = GREATEST(`max`, values(`max`);

Затем сделайте ночную работу, чтобы установить среднее значение.

Таким образом, вы касаетесь 1 ряда, а не до 1440.

Другой метод - собрать показания за минуту, а затем применить их в одном запросе.

У вас есть миллионы датчиков? Переосмыслите использование 4-байтового INT для Sensor_ID; есть целые числа поменьше.

Где вы нашли эти датчики? Я сомневаюсь, что вам нужно больше, чем 7 значащих цифр FLOAT (4 байта) вместо 8-байтового DOUBLEs.

Моя точка зрения о типах данных заключается в том, что сжатие данных также ускорит процесс, особенно если вы достигнете того, что слишком много данных для кэширования в ОЗУ.

Фразировка: «Sensor_ID и Date как первичные ключи» подразумевают, что есть два разных PK, что невозможно. Вместо этого «Sensor_ID и Date образуют составной первичный ключ». И да, это то, что вам нужно для этого стола. Ставите ли вы Date первым или последним, зависит от того, какой типичный SELECT.

FOREIGN KEYs - другая стоимость. Каждый раз, когда вставка выполняется, необходимо проверить другую таблицу, чтобы проверить существующий идентификатор. К настоящему времени вы достаточно отладили свой код; ФК, возможно, пустая трата.

avg можно вычислять каждую минуту, но (1) это несколько бессмысленно, пока день не закончится, и (2) потребуется дополнительный столбец (с подсчетом).

0 голосов
/ 10 января 2019

Добавьте индекс к столбцу DateTime в таблице readings.

Затем попробуйте следующий SQL:

insert into extremes(Date, Sensor_ID, `min`, `max`, `avg`)
    select date(DateTime) as `Date`, Sensor_ID as Sensor_ID,
        min(Value) as `min`, max(Value) as `max`, avg(Value) as `avg`
        from readings where `DateTime` >= date_format(curdate(), '%Y-%m-%d 00:00:00')
    group by date(DateTime), Sensor_ID
on duplicate key update 
    `min` = values(`min`), `max` = values(`max`), `avg` = values(`avg`);
...