В MySQL, каков наиболее эффективный дизайн запроса для объединения больших таблиц со многими отношениями между предикатами объединения? - PullRequest
9 голосов
/ 13 марта 2010

В нашем приложении мы собираем данные о рабочих характеристиках автомобильных двигателей - в основном исходные данные о рабочих характеристиках двигателей на основе типа двигателя, транспортного средства, на котором он работает, и конструкции двигателя. В настоящее время основой для вставок новых рядов является период включения и выключения двигателя; мы отслеживаем переменные производительности, основываясь на изменении состояния двигателя с активного на неактивное и наоборот. Соответствующая таблица engineState выглядит следующим образом:

+---------+-----------+---------------+---------------------+---------------------+-----------------+
| vehicle | engine    | engine_state  | state_start_time    | state_end_time      | engine_variable |
+---------+-----------+---------------+---------------------+---------------------+-----------------+
| 080025  | E01       | active        | 2008-01-24 16:19:15 | 2008-01-24 16:24:45 |             720 | 
| 080028  | E02       | inactive      | 2008-01-24 16:19:25 | 2008-01-24 16:22:17 |             304 |
+---------+-----------+---------------+---------------------+---------------------+-----------------+ 

Для конкретного анализа мы хотели бы проанализировать содержимое таблицы, основываясь на степени детализации строк в минутах, а не на текущей основе активного / неактивного состояния механизма. Для этого мы думаем о создании простой таблицы productionMinute со строкой для каждой минуты в периоде, который мы анализируем, и объединении таблиц productionMinute и engineEvent в столбцах даты и времени в каждой таблице. Поэтому, если наш период анализа с 2009-12-01 по 2010-02-28, мы создадим новую таблицу со 129 600 строками, по одной на каждую минуту каждого дня в течение этого трехмесячного периода. Первые несколько строк таблицы productionMinute:

+---------------------+ 
| production_minute   |
+---------------------+
| 2009-12-01 00:00    |
| 2009-12-01 00:01    |
| 2009-12-01 00:02    |     
| 2009-12-01 00:03    |
+---------------------+

Соединение между таблицами будет:

     FROM engineState AS es 
LEFT JOIN productionMinute AS pm ON pm.production_minute >= es.state_start_time 
                                AND pm.production_minute <= es.event_end_time 

Это объединение, однако, поднимает множество экологических проблем:

  1. Таблица engineState содержит 5 миллионов строк, а таблица productionMinute - 130 000 строк
  2. Когда строка engineState занимает более одной минуты (т. Е. Разница между es.state_start_time и es.state_end_time превышает одну минуту), как в случае с приведенным выше примером, существует несколько строк таблицы productionMinute которые объединяются в одну engineState строку таблицы
  3. Когда в течение данной минуты работает несколько двигателей, также как в приведенном выше примере, несколько строк таблицы engineState объединяются в одну строку productionMinute

При тестировании нашей логики и использовании только небольшого извлечения таблицы (один день, а не 3 месяца, для таблицы productionMinute) на генерацию запроса уходит более часа. При исследовании этого элемента с целью повышения производительности, чтобы можно было запрашивать данные за три месяца, мы думали создать временную таблицу из таблицы engineEvent, исключив любые данные таблицы, которые не являются критическими для анализа, и присоединение временной таблицы к таблице productionMinute. Мы также планируем поэкспериментировать с различными объединениями, в частности, с внутренним объединением, чтобы посмотреть, улучшит ли это производительность.

Каков наилучший дизайн запроса для объединения таблиц со многими: многими отношениями между предикатами объединения, как описано выше? Какой тип соединения лучше (левый / правый, внутренний)?

Ответы [ 6 ]

1 голос
/ 29 марта 2010

Я согласен с vy32. Вам необходимо выполнить этот запрос один раз и только один раз, чтобы получить данные в формате, подходящем для анализа. Вы должны использовать подходящий инструмент ETL (или, черт возьми, просто perl или что-то простое), чтобы получить данные из таблицы engineState, рассчитать производственную минуту, а затем загрузить ее в другую БД, которая должным образом смоделирована для запросов типа анализа.

Если вы думаете о своей проблеме, вы просто денормализуете свои данные и назначаете номера минут в качестве суррогатных ключей. Это относительно простая (и распространенная) проблема ETL, которая не работает в прямом SQL, но проста с другими языками и инструментами.

Ваш объем производства будет легко обработан с помощью настоящего процесса ETL.

1 голос
/ 23 марта 2010

Производительность поиска данных является функцией

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

Для всего этого вы можете оптимизировать

  • добавление индексов
  • уменьшение размера таблицы путем ее вертикального разбиения (разделение таблицы на две или более семантически разные таблицы - например, если из вашей таблицы на 5 м вы фактически работаете только с записями по 100 тыс., То в 99,5% случаев вы можете разбить таблицу на активный / неактивный или аналогичный)
  • при условии, что вы не можете разделить по вертикали, вы можете разделить таблицу по горизонтали - количество столбцов в этой таблице также влияет на скорость поиска (но не так сильно)
  • наконец, улучшение скорости ввода-вывода может быть достигнуто путем прозрачного разделения таблицы по нескольким жестким дискам (но вы должны знать свойства вашей файловой системы)

Индексы оказывают наибольшее влияние на производительность, поскольку они могут на несколько порядков сократить время доступа к диску и скорость операций с памятью (они превращают O (n) в журнал O (n) за счет обслуживания структуры индекса; они замедляют обновления)

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

Для вашего конкретного примера попробуйте сравнить различные комбинации индексов

  1. pm.production_minute должен быть обязательно проиндексирован
  2. с es.state_start_time и es.state_end_time у вас есть 4 возможных варианта индекса (которые вы можете комбинировать):
    индекс es.state_start_time
    индекс es.state_end_time
    индекс по (es.state_start_time, es.state_end_time)
    индекс по (es.state_end_time, es.state_start_time)

Знание ваших данных позволит вам определить, какой из них является оптимальным. Я не удивлюсь, если вы обнаружите, что последние два столбца индекса будут работать лучше. Или с одним столбцом и с двумя индексами (но в обратном порядке столбцов).

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

0 голосов
/ 30 марта 2010

Использование LEFT JOIN, INNER JOIN или RIGHT JOIN - это семантическая разница - использование другого соединения для производительности - не просто плохая идея, это означает, что связь между таблицами не была полностью понятно - поскольку разные типы JOIN могут возвращать разную информацию, потому что они означают разные вещи.

Как правило, ВНУТРЕННИЕ СОЕДИНЕНИЯ очень удобны для оптимизатора, поскольку это позволяет значительно расширить критерии фильтра, отличные от вашего предложения WHERE и условия JOIN, для улучшения сканирования индекса или сканирования таблицы. Ограничения ссылочной целостности могут также дать оптимизатору информацию о том, что данные гарантированно существуют на обеих сторонах.

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

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

Когда ваши отчеты становятся слишком плохими в хорошо проиндексированной нормальной форме, я бы хотел преобразовать данные, возможно, в размерную модель (взгляните на методологию Кимбалла) со звездными схемами, которые имеют очень простые схемы для отчетности ( как правило, все ВНУТРЕННИЕ СОЕДИНЕНИЯ и простая звезда) и могут быть очень хорошо оптимизированы на традиционных системах баз данных.

0 голосов
/ 30 марта 2010

Если я правильно понял, вы смотрите на проблему BI. Планирование BI должно иметь оперативные данные отдельно от консолидированных.

Чтобы это произошло (быстро и грязно), вам понадобятся три элемента.

  • Ваши оперативные данные
  • Задание ETL, для которого требуется только выполнить запрос, который вы показали, и вставить набор результатов в другую денормализованную таблицу
  • Денормализованные таблицы, в которые вы будете сохранять сводные данные.

Таким образом, вы ускорите свой запрос, поскольку теперь это будет простой выбор.

Как и в любом решении BI, вам необходимо ежедневно запускать ETL (в зависимости от ваших деловых потребностей) для обновления денормализованной информации.

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

0 голосов
/ 26 марта 2010

Мой опыт показывает, что оптимизатор запросов MySQL довольно плох. Тот, что в PostgreSQL, намного лучше.

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

0 голосов
/ 14 марта 2010

Производительность будет зависеть от того, как структурированы ваши данные в таблицах.

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...