Я хочу добавить некоторые служебные методы, чтобы избежать многих n + 1 проблем в устаревших приложениях.
Общая схема такова:
select a.* /* over 10 columns */
from [table-A] a
where /* something */
Извлечено в коллекцию ClassA
экземпляров записей
Затем подэкземпляры извлекаются лениво:
select b.* /* over 10 columns */
from [sub-table-B] b
where b.ParentId = @ClassA_ID
Это приводит к проблеме выбора n + 1.В большинстве случаев это не является серьезной проблемой, поскольку на нечасто посещаемых страницах извлекается только пара ClassA
экземпляров, но во все большем числе мест эта проблема n + 1 приводит к тому, что страницы становятся слишком медленными из-за масштабирования приложения,
Я собираюсь заменить часть существующего кода доступа к данным этого приложения, чтобы экземпляры ClassA
и ClassB
извлекались вместе.
Я думаю, что есть 3как это можно сделать:
1) Получить экземпляры ClassA
, как мы делаем сейчас, затем получить экземпляры ClassB
в одном агрегированном вызове:
select b.*
from [sub-table-B] b
where b.ParentId in ( /* list of parent IDs */ )
Это два отдельныхВызовы БД, и план запроса динамического SQL не будет кэшироваться (из-за списка идентификаторов).
2) Получить экземпляры ClassB
с подзапросом:
select b.*
from [sub-table-B] b
inner join [table-A] a
on b.ParentId = a.[ID]
where /* something */
Это также два вызова БД, и запрос к [table-A]
должен оцениваться дважды.
3) Собрать все вместе и исключить дублирование ClassA
экземпляров:
select a.*, b.*
from [table-A] a
left outer join [sub-table-B] b
on a.[ID] = b.ParentId
where /* something */
Это всего лишь один вызов БД, но теперь мы получаем повторное содержимое [table-A]
- набор результатов будет больше, а время отправки данных из БД клиенту будет больше.
Так что на самом делеэто 3 возможных компромисса:
- 2 вызова БД, без кэширования запросов
- 2 вызова БД, сложный запрос оценивается дважды
- 1 вызов БД, значительно больший набор результатов
Я могу протестировать эти три шаблона для любой пары таблиц родитель-потомок, ноУ меня их много.Что я хочу знать, так это то, какой шаблон всегда быстрее?Что важнее, почему?Является ли один из этих компромиссов очевидным фактором снижения производительности?
Что используют существующие механизмы, такие как Linq, EF и NHibernate?
Существует ли 4-й способ, который лучше всех 3?