Оптимизация MySQL-запроса с использованием функций GROUP BY по времени - PullRequest
3 голосов
/ 22 декабря 2010

У меня следующий запрос:

SELECT location, step, COUNT(*), AVG(foo), YEAR(start), MONTH(start), DAY(start)
FROM table WHERE jobid = 'xxx' AND start BETWEEEN '2010-01-01' AND '2010-01-08'
GROUP BY location, step, YEAR(start), MONTH(start), DAY(start)

Изначально у меня были индексы по отдельным столбцам, такие как jobid и start , но я быстро понял, что MySQL действительно учитывает только один индекс на таблицу в выборке. Таким образом, он будет использовать индекс jobid , а затем выполнит довольно большое сканирование для фильтрации по диапазону start .

Добавление индекса для ( jobid , start ) очень помогло, но GROUP BY по-прежнему вызывает проблемы с производительностью. Я прочитал документы по оптимизации GROUP BY и понимаю, что для получения выгоды от этих оптимизаций мне нужен индекс, который содержит ( location , step , start ), но у меня все еще есть два открытых вопроса:

  1. Будет ли группа при оптимизации работать даже с функциями времени (ГОД, МЕСЯЦ, ДЕНЬ и т. Д.)? Или мне придется хранить эти значения в виде отдельных столбцов? Причина, по которой мне нравится выполнять функции, заключается в том, что я могу управлять часовым поясом для каждого соединения и получать результаты, адаптированные к часовому поясу конечных пользователей. Если мне нужно предварительно сохранить год, месяц и день, я сделаю это через UTC, и тогда все мои пользователи будут просто получать отчеты в формате UTC.

  2. Даже если я смогу решить проблему № 1, могу ли я это сделать? Индекс ( jobid , start ) помог с предложением WHERE, но GROUP BY требуется другой индекс для оптимизации ( location , step , начало ) или, в зависимости от ответа на # 1, ( местоположение , шаг , год , месяц , , день ). Но проблема в том, что эти два индекса не имеют общего левого набора столбцов, поэтому я не верю, что мои WHERE и GROUP by могут быть совместимы, так что используется один и тот же индекс. Итак, мой вопрос: я только что накрылся здесь?

Любые другие мысли о том, как этого добиться, были бы полезны. И, просто, чтобы предупредить несколько вопросов / комментариев, которые могут возникнуть:

  1. Да, это набор данных временных рядов.
  2. Да, было бы полезно что-то вроде RRDtool , но из-за этого я потерял бы результаты, зависящие от часового пояса.
  3. Да, предварительный расчет накопительных пакетов, вероятно, был бы хорошей идеей, но мне не нужна потрясающая производительность, и поэтому я в порядке с хорошей производительностью, если она позволяет мне настраивать результаты для часового пояса каждого пользователя.

С учетом вышесказанного, если у кого-то есть какие-либо конструктивные предложения о том, как сделать что-то вроде свертки или циклических баз данных и при этом получить результаты, зависящие от часового пояса, я весь слух!


Обновление : по запросу, вот еще некоторая информация:

показать индексы с выхода:

step    0   PRIMARY 1   step_id A   16  NULL    NULL        BTREE   
step    1   start   1   start   A   16  NULL    NULL        BTREE   
step    1   step    1   step    A   2   NULL    NULL        BTREE   
step    1   foo 1   foo A   16  NULL    NULL    YES BTREE   
step    1   location    1   location    A   2   NULL    NULL    YES BTREE   
step    1   jobid   1   jobid   A   2   NULL    NULL    YES BTREE   

показать создать вывод таблицы:

CREATE TABLE `step` (
  `start` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `step` smallint(2) unsigned NOT NULL,
  `step_id` int(8) unsigned NOT NULL AUTO_INCREMENT,
  `location` varchar(12) DEFAULT NULL,
  `jobid` varchar(37) DEFAULT NULL,
  PRIMARY KEY (`step_id`),
  KEY `start_time` (`start`),
  KEY `step` (`step`),
  KEY `location` (`location`),
  KEY `job_id` (`jobid`)
) ENGINE=InnoDB AUTO_INCREMENT=240 DEFAULT CHARSET=utf8

Ответы [ 4 ]

1 голос
/ 22 декабря 2010

Вместо этого

GROUP BY location, step, YEAR(start), MONTH(start), DAY(start)
ORDER BY location, step, YEAR(start), MONTH(start), DAY(start)

попробовать

GROUP BY location, step, date_format(start, '%Y%m%d')
ORDER BY location, step, date_format(start, '%Y%m%d')
0 голосов
/ 15 февраля 2011

Существует вероятность, что это может быть выбрано быстрее, если местоположение и шаг являются целочисленными внешними ключами в других таблицах, имеющих только имя и целочисленный идентификатор.

Сначала запрос будет сгруппирован на целочисленных данных, которые будут сравниваться намного быстрее,Во-вторых, есть вероятность, что механизм БД может автоматически индексировать эти числа.

Я бы также рассмотрел выгрузку jobid в отдельную таблицу в случае повторения значения.

0 голосов
/ 22 декабря 2010

создайте один составной индекс для jobid, start, location, step

, затем сгруппируйте сначала по этому порядку и отсортируйте его:

SELECT location, step, COUNT(*), AVG(foo), YEAR(start), MONTH(start), DAY(start)
FROM table WHERE jobid = 'xxx' AND start BETWEEEN '2010-01-01' AND '2010-01-08'
GROUP BY YEAR(start), MONTH(start), DAY(start), location, step
ORDER BY location, step, YEAR(start), MONTH(start), DAY(start)

ОБНОВЛЕНИЕ

Похоже, MySql не может использоватьиндекс при использовании функций YEAR, MONTH и DAY.начиная с

  1. После удаления начала из предложения WHERE в объяснении по-прежнему отображается using filesort
  2. Добавление 3 столбцов: y = YEAR(start), m = MONTH(start), d=DAY(start), создание индекса для jobid, y, m, d, location, step и обновлениеWHERE ... AND y = 2010 AND m = 12 AND d BETWEEN 1 AND 08 действительно удаляет using temporary using filesort.

, сохраняя 3 дополнительных столбца, кажется плохой идеей, поскольку разница в производительности между GROUP BY не должна иметь большого значения, использует ли она временный или нет.

0 голосов
/ 22 декабря 2010

и понимаю, что для получения выгоды от этих оптимизаций мне нужен индекс, который содержит (местоположение, шаг, начало)

Нет.Вы можете создать составной индекс jobid + start + location + step, и он поможет , если бы не было BETWEEN.Поскольку вы используете условие диапазона в WHERE - никакие индексы не будут использоваться для GROUP BY, и единственное и лучшее, что вы можете сделать для этого запроса, это просто jobid + start index.

Лучшее решениеimho, это разложить эту таблицу до некоторой предварительно рассчитанной формы.Например: агрегировать данные по часам планировщика.

...