Почему этот запрос тайм-аут? V2 - PullRequest
1 голос
/ 03 августа 2010

Этот вопрос является продолжением Этот вопрос

Решение, очистка кэша плана выполнения, казалось, работало в то время, но я снова и снова сталкивался с одной и той же проблемой, и очистка кэша больше не помогает. Здесь должна быть более глубокая проблема.

Я обнаружил, что если я удаляю .Distinct () из запроса, он возвращает строки (с дубликатами) примерно через 2 секунды. Однако, с .Distinct () это может занять до 4 минут. В таблицах много строк, а некоторые из полей предложения where не имеют индексов. Однако количество возвращаемых записей довольно мало (максимум несколько десятков).

Неясность в том, что если я получаю SQL, сгенерированный запросом Linq, через Linqpad, то выполняю этот код как SQL или в SQL Management Studio (включая DISTINCT), он выполняется примерно за 3 секунды.

В чем разница между запросом Linq и выполненным SQL?

У меня есть краткосрочный обходной путь, и он возвращает набор без .Distinct () в виде списка, а затем использует .Distinct в списке, это занимает около 2 секунд. Однако мне не нравится работать с SQL Server на веб-сервере.

Я хочу понять, ПОЧЕМУ Distinct на 2 порядка медленнее в Linq, но не в SQL.

UPDATE:

При выполнении кода через Linq профилировщик sql показывает этот код, который в основном является идентичным запросом.

sp_executesql N'SELECT DISTINCT [t5].[AccountGroupID], [t5].[AccountGroup] 
    AS [AccountGroup1]
FROM [dbo].[TransmittalDetail] AS [t0]
INNER JOIN [dbo].[TransmittalHeader] AS [t1] ON [t1].[TransmittalHeaderID] = 
    [t0].[TransmittalHeaderID]
INNER JOIN [dbo].[LineItem] AS [t2] ON [t2].[LineItemID] = [t0].[LineItemID]
LEFT OUTER JOIN [dbo].[AccountType] AS [t3] ON [t3].[AccountTypeID] = 
    [t2].[AccountTypeID]
LEFT OUTER JOIN [dbo].[AccountCategory] AS [t4] ON [t4].[AccountCategoryID] = 
    [t3].[AccountCategoryID]
LEFT OUTER JOIN [dbo].[AccountGroup] AS [t5] ON [t5].[AccountGroupID] = 
    [t4].[AccountGroupID]
LEFT OUTER JOIN [dbo].[AccountSummary] AS [t6] ON [t6].[AccountSummaryID] = 
    [t5].[AccountSummaryID]
WHERE ([t1].[TransmittalEntityID] = @p0) AND ([t1].[DateRangeBeginTimeID] = @p1) AND 
([t1].[ScenarioID] = @p2) AND ([t6].[AccountSummaryID] = @p3)',N'@p0 int,@p1 int,
   @p2 int,@p3 int',@p0=196,@p1=20100101,@p2=2,@p3=0

UPDATE:

Единственное различие между запросами состоит в том, что Linq выполняет его с sp_executesql, а SSMS - нет, в противном случае запрос идентичен.

UPDATE:

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

Ответы [ 5 ]

1 голос
/ 03 августа 2010

Плохой план, скорее всего, является результатом перехвата параметров: http://blogs.msdn.com/b/queryoptteam/archive/2006/03/31/565991.aspx

К сожалению, на самом деле не существует какого-либо хорошего универсального способа (который я знаю), чтобы избежать этого с помощью L2S. context.ExecuteCommand ("sp_recompile ...") было бы уродливым, но возможным обходным решением, если запрос выполняется не очень часто.

Возможно небольшое изменение запроса для принудительной перекомпиляцииеще один.

Перемещение частей (или всех) запроса в представление *, функцию * или хранимую процедуру * Сторона DB будет еще одним обходным путем.
* = , где вы можетеиспользовать локальные параметры (func / proc) или подсказки оптимизатора (все три), чтобы заставить 'хороший' план

Кстати, вы пытались обновить статистику для соответствующих таблиц?Статистика автоматического обновления SQL Server не всегда справляется с этой задачей, поэтому, если у вас нет запланированного задания, возможно, стоит подумать о сценариях и планировании статистики обновлений ... ... настройка размера выборки по мере необходимости также можетhelp.

Могут быть способы решения проблемы путем добавления * (или отбрасывания *) правильных индексов в соответствующие таблицы, но без знания базовой схемы БД, размера таблицы, распределения данных и т. д., что немногоТрудно дать более конкретный совет по поводу ...
* = Отсутствующие и / или перекрывающиеся / избыточные индексы могут привести к плохим планам выполнения.

1 голос
/ 03 августа 2010

Вы уверены, что хотите использовать LEFT OUTER JOIN здесь?Этот запрос выглядит так, как будто он, вероятно, должен использовать INNER JOIN, особенно потому, что вы берете столбцы, которые потенциально имеют значение NULL, а затем выполняете различные операции над ним.

1 голос
/ 03 августа 2010

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

[t6].[AccountSummaryID] = @p3

может быть

[t5].[AccountSummaryID] = @p3

Возвращаемые значения взяты из таблицы [t5]. [t6] используется только фильтр для этого параметра, который выглядит так, как будто это внешний ключ от t5 до t6, поэтому он присутствует в [t5]. Следовательно, вы можете полностью удалить соединение с [t6]. Или я что-то упустил?

1 голос
/ 03 августа 2010

SQL, который дает вам Linqpad, может быть не совсем тем, что отправляется в БД.

Вот что я хотел бы предложить:

  1. Запускать SQL Profiler для БД, пока вывыполнить запрос.Найдите утверждение, которое соответствует вашему запросу
  2. Вставьте весь отчет в SSMS и включите опцию «Показать фактический план выполнения».
  3. Опубликуйте результирующий план здесь, чтобы люди могли его проанализировать.

Ключевые вещи, на которые нужно обратить внимание:

  • Сканирование таблиц, обычно подразумевающее отсутствие индекса
  • Широкие стрелки в графическом плане, указывающие лотыобрабатываемых промежуточных строк.

Если вы используете SQL 2008, при просмотре плана вам часто сообщают, отсутствуют ли какие-либо индексы, которые следует добавить для ускорения запроса.

Кроме того, выполняете ли вы работу с БД, загруженной другими пользователями?

0 голосов
/ 03 августа 2010

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

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

В дополнение к этому, если вы вручную создаете SQL, вы можете попробовать переместить условия из предложения WHERE в соответствующие предложения JOIN. Это фактически меняет способ выполнения запроса SQL Server и может создать более эффективный план выполнения. Я видел случаи, когда перемещение фильтров из предложения WHERE в JOIN приводило к тому, что SQL Server раньше фильтровал таблицу в плане выполнения и значительно изменял время выполнения.

...