ORM Выберите n + 1 производительность; присоединиться или не присоединиться

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

Сценарий:

Клиент - Заказ (где Заказ имеет идентификатор клиента) - OrderPart - Part

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

Теперь у меня есть два основных варианта:

  • Используйте вложенный цикл (который создает отдельные запросы)
  • Используйте параметры загрузки данных (которые создают одно соединение для запроса)

Вопрос:

Большинство советов и примеров ORM предлагают использовать вариант 2, и я могу понять, почему. Однако вариант 2 потенциально может отсылать огромное количество дублированных данных, например:

Результаты Варианта 1 (3 запроса):

ID  Name       Country
1   Customer1  UK

ID  Name
1   Order1
2   Order2

ID  Name
1   Part1
2   Part2
3   Part3

Результаты второго варианта (1 запрос):

ID  Name       Country  ID  Name    ID Name
1   Customer1  UK       1   Order1  1  Part1
1   Customer1  UK       1   Order1  2  Part2
1   Customer1  UK       1   Order1  3  Part3
1   Customer1  UK       2   Order2  1  Part1
1   Customer1  UK       2   Order2  2  Part2

Вариант 1 отправляет обратно 13 полей с 3 запросами. Вариант 2 отправляет обратно 42 поля в 1 запрос. Теперь представьте, что таблица Customer имеет 30 полей, а Orders имеет более сложные подзаголовки, дублирование данных может быстро стать огромным.

Какое влияние на общую производительность оказывают следующие вещи:

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

Является ли вариант 2 всегда лучшим выбором, вариант 1 - лучший выбор или это зависит от ситуации? Если это зависит от того, какие критерии следует использовать для определения? Являются ли какие-либо ОРМ достаточно умными, чтобы справиться с этим самим?

Ответ 1

Накладные расходы на подключение к базе данных

Очень мало, если они находятся в одной подсети, которой они обычно являются. Если это не так, то это все еще не огромные накладные расходы и их можно преодолеть с помощью кэширования, которые имеют большинство ORM (NHibernate имеет 1-й и 2-й уровень кэширования).

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

Для SELECT N+1 это, очевидно, будет длиннее, поскольку ему придется отправлять оператор select каждый раз, что может быть длиной до 1k. Он также должен будет захватить новое соединение из пула. Chatty против короткого использования, чтобы быть аргументом в 2002-2003 годах, но теперь это действительно не имеет большого значения, если это не очень большое приложение, и в этом случае вам, вероятно, понадобится более опытный (или лучше заплаченный) ученый, дающий свои взгляды - т.е. консультант.

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

Кстати, SELECT N+1, вероятно, самая распространенная проблема производительности, с которой люди сталкиваются с NHibernate, когда они впервые начинают использовать ее (включая меня), и это то, что на самом деле требует настройки для сортировки. Это связано с тем, что NHibernate относится к ORM, что С++ относится к языкам.

Bandwidth

Дополнительный оператор SELECT для каждого Customer в конечном итоге будет создан для многих объектов Customer * Orders. Поэтому для большой системы это может быть заметно, но, как я уже упоминал, ORM обычно имеют механизмы кэширования, чтобы свести на нет эту проблему. Количество выражений SELECT также не будет таким огромным, учитывая:

  • Вы в той же сети, что и сервер SQL, большую часть времени
  • Увеличение количества байтов составляет около 0,5-50 килобайт дополнительной полосы пропускания? Подумайте, как быстро на большинстве серверов.

Ответ 2

многое из этого будет зависеть от количества данных, которые вы просматриваете. Соединение, возвращая больше полей, будет работать значительно быстрее (как правило), чем набор запросов Option 1. Из моего личного опыта замедление почти всегда на этом уровне, фактический запуск запроса, а не простое количество данных, передаваемых по любой трубе, которую вы имеете.