MySQL быстро извлекает последнюю запись в каждой группе - PullRequest
2 голосов
/ 04 октября 2019

У меня есть таблица с журналами данных GPS устройств logs(id - PK AI, device_id - FK, lat - DECIMAL, long - DECIMAL, time - DATETIME). Идентификатор является первичным ключом, и есть index on (id, device_id), и я хочу получить последние записи для каждого устройства.

Мой текущий запрос:

SELECT * FROM devices
WHERE devices.id IN (
    SELECT MAX(id) FROM devices GROUP BY device_id
)

В таблице с 1,5 миллионами записейи для 5 уникальных устройств запрос занимает 8 секунд. Можно ли его оптимизировать? Устройства отправляют данные каждую секунду, и я ожидаю, что общее количество устройств составит 2000.

Это слишком медленно, даже с 5 устройствами.

Другой мой запрос - это выбор маршрута устройства за период времени. Это также занимает 10 секунд в течение дня.

Является ли MySQL правильным выбором для этой задачи? Должен ли я выбрать другую базу данных? Есть ли способ сделать запросы быстрее?

SQL-скрипта

Ответы [ 2 ]

2 голосов
/ 04 октября 2019

Я бы порекомендовал следующее. Во-первых, перепишите код как:

SELECT d.* 
FROM devices d
WHERE d.id = (SELECT MAX(d2.id) FROM devices d2 WHERE d2.device_id = d.device_id);

Но сначала создайте индекс для devices(device_id, id).

РЕДАКТИРОВАТЬ:

Интересно, поможет ли какая-то внешняя оптимизация, такаяиспользуя datetime:

SELECT d.* 
FROM devices d
WHERE d.datetime >= NOW() - INTERVAL 1 HOUR AND
      d.id = (SELECT MAX(d2.id)
              FROM devices d2
              WHERE d2.device_id = d.device_id AND
                    d2.datetime >= NOW() - INTERVAL 1 HOUR
             );

Для этого вам также понадобится индекс для devices(datetime, device_id).

1 голос
/ 05 октября 2019

Попробуйте каждый из этих двух запросов. Обычно, по крайней мере, один будет работать хорошо для меня с вашими запросами "max row".

Запрос 1:

SELECT
d.*
FROM devices d
LEFT OUTER JOIN devices larger_d
ON larger_d.device_id = d.device_id
AND larger_d.id > d.id
WHERE larger_d.device_id IS NULL

Запрос 2:

SELECT
d.*
FROM devices d
INNER JOIN (
SELECT
MAX(id) AS id,
device_id
FROM devices d
GROUP BY device_id
) largest_d
ON largest_d.device_id = d.device_id
AND largest_d.id = d.id

В обоихВ некоторых случаях вам потребуется индекс на (device_id,id) перед выполнением этих запросов.

В ответ на ваши комментарии к ответам других людей индекс (id,device_id) не эквивалентен тому, который мы предлагаем. Вам не нужно удалять его, однако это замедлит вставки (как и все индексы). Однако для этого запроса он бесполезен, и поэтому вы, вероятно, можете удалить его, если у вас нет особых причин его сохранять.

...