Как оптимизировать время ответа этого запроса SQL большой таблицы? - PullRequest
0 голосов
/ 10 октября 2019

У меня средняя и большая таблица (около 350000 записей и растет). Мне нужно получить каждую последнюю запись для пары dev_id и var_id из таблицы. Я на самом деле могу их получить, но запрос занимает около 20 секунд, и это неприемлемо для моих целей.

Я пытаюсь следующий запрос на сервере MySQL с MariaDB:

select d.dev_id, d.var_id, d.ts, d.value from data_table d 
                where d.ts > NOW() - INTERVAL 2 DAY
                and ts = (SELECT MAX(ts) FROM data_table WHERE dev_id = d.dev_id AND var_id = d.var_id)
                ORDER BY  d.dev_id

Таблица имеет следующую структуру:

id  | dev_id | frame_number | var_id | value | ts
1   |    2   |      1       |    2   | 65.5  | 2019-10-10 19:56:05
2   |    3   |      5       |    4   | 23    | 2019-10-10 20:56:06
3   |    2   |      1       |    2   | 65.5  | 2019-10-10 20:59:30
.   |    .   |      .       |    .   |   .   |    .
.   |    .   |      .       |    .   |   .   |    .
.   |    .   |      .       |    .   |   .   |    .
300k|    5   |      100     |    7   | -15.23| 2020-10-10 20:59:30

Мне нужно получить более быстрый ответ на подобный запрос, но моего опыта недостаточно, чтобы обнаружить узкое место в запросе

Заранее благодарим вас за все

РЕДАКТИРОВАТЬ 1: Я не могу пропустить ORDER BY, но улучшение пропускает низкое (20 секунд против 18,5 секунд)

РЕДАКТИРОВАТЬ 2: схема данных_таблицы data_table schema

РЕДАКТИРОВАТЬ 3 и как это исправить: Добавлено (dev_id, var_id и ts) в качестве индекса (индекс основан на нескольких столбцах). Запрос теперь требует только 0,6 секунд data_table schema for the solution

Ответы [ 2 ]

4 голосов
/ 10 октября 2019

Для этого запроса:

select d.dev_id, d.var_id, d.ts, d.value
from data_table d 
where d.ts > NOW() - INTERVAL 2 DAY and
      ts = (SELECT MAX(d2.ts)
            FROM data_table d2
            WHERE d2.dev_id = d.dev_id AND d2.var_id = d.var_id
           )
ORDER BY d.dev_id;

Я бы рекомендовал два индекса:

  • data_table(ts, dev_id, var_id, value)
  • data_table(dev_id, var_id, ts)

Первый - это индекс покрытия для внешнего запроса. Второй - это индекс покрытия для внутреннего запроса.

Это может помочь - если у вас много дубликатов. Если нет, внешний order by может быть причиной проблем с производительностью. Это было бы немного сложнее исправить.

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

Я склонен винить коррелированные подзапросы в этих случаях, хотя уже предложенные индексы помогут в обоих сценариях:

SELECT d.dev_id, d.var_id, d.ts, d.value 
FROM (
   SELECT dev_id, var_id, MAX(ts) AS ts
   FROM data_table
   WHERE ts >  NOW() - INTERVAL 2 DAY
   GROUP BY dev_id, var_id
) AS lastTS
INNER JOIN data_table AS d 
    ON lastTS.dev_id = d.dev_id AND lastTS.var_id = d.var_id AND lastTS.ts = d.ts
    -- or, alternatively, USING (dev_id, var_id, ts)
ORDER BY d.dev_id
;

Ваш текущий коррелированный подзапрос выполняется («под капотом») отдельно длякаждая запись за последние два дня '(dev_id, var_id);возможно, даже несколько раз для нескольких последних записей с одинаковыми (dev_id, var_id). Предлагаемая мной версия рассчитывает максимальные значения для каждого (var_id, dev_id), которые произошли за последние 2 дня, и затем объединяет их с таблицей, чтобы найти полные записи.

Если внешний запрос имеет мало промежуточных результатовболее сфокусированный коррелированный запрос может быть быстрее, чем некоррелированный, для большого объема данных;но если имеется значительное количество промежуточных результатов и / или коррелированный подзапрос не приведет к значительному снижению стоимости некоррелированной версии, я считаю, что некоррелированные версии работают лучше.

...