Скрипт SQL «найти отсутствующий индекс» предлагает отсутствующий индекс, который уже существует? Запрос в списке 10 лучших процессоров

#sql #sql-server #performance #indexing #sql-execution-plan

#sql #sql-server #Производительность #индексирование #sql-execution-plan

Вопрос:

Один запрос находится в верхней части списка самых ресурсоемких процессоров, но его простота (см. Пример Ниже) противоречит этому. Однако сценарий «найти отсутствующий индекс» предлагает создать индекс для таблицы… но этот точный (порядок столбцов и включение) индекс уже существует.

 SELECT COUNT(Id) 
  FROM dbo.ProductOrder
 WHERE userId = @userId
   AND status = @status
 

Сценарий «найти отсутствующий индекс» предлагает индекс по идентификатору пользователя и статусу.

Пару недель назад мы заметили, что отсутствующий индекс указывает на ошибку, и где-то нашли сообщение (где-то), что это ошибка в SQL Server R2 SP1 (версия, которую мы используем).

Но теперь запрос находится (и остается) в топ-10… Я больше не уверен.

То, что мы пробовали:

  • Добавление идентификатора в список включения (не рекомендуется)
  • Уровень изоляции НЕЗАФИКСИРОВАННОЙ транзакции NOLOCK / READ
  • sp_recompile в таблице

У нас жесткий график обслуживания, обеспечивающий минимальную фрагментацию.

Что здесь может произойти?

Редактировать: я упомянул план выполнения, когда начал публикацию, но в планах выполнения фактически не упоминается индекс (больше). Скрипт «найти отсутствующий индекс» выполняет.

Редактирование 2: определение индекса

 CREATE NONCLUSTERED INDEX [ProductOrder_UserStatus_Nidx] ON [dbo].[ProductOrder]
(
    [userId] ASC,
    [status] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
GO
 

Редактирование 3: отсутствует вывод индекса

 statement: dbo.ProductOrder
improvement_measure: 187243,941382055
create_index_statement: CREATE INDEX [missing_index_4_3_ProductOrder] ON [dbo].[ProductOrder] ([userId], [status])
group_handle: 4
unique_compiles: 10
user_seeks: 51161
user_scans: 0
last_user_seek: 2014-06-22 10:06:12.390
last_user_scan: NULL
avg_total_user_cost: 8,77252167199463
avg_user_impact: 41,72
system_seeks: 0
system_scans: 0
last_system_seek: NULL
last_system_scan: NULL
avg_total_system_cost: 0
avg_system_impact: 0
database_id: 8
object_id: 2014122416
 

Обновление: новый скрипт нашел правильный запрос

Мы смотрим на неправильный запрос. Новый скрипт нашел тот, который фактически создает оператор отсутствующего индекса в плане запроса:

 CREATE PROCEDURE [dbo].[ProductOrder_GetByUserAndStatus]
  @userId INT, 
  @status INT,
  @deliveryStatus INT
AS

 SELECT U.id
      , U.email
      , U.[language]
      , U.username
      , U.firstname   ' '   ISNULL(u.middlename, '')   ' '   u.lastname AS fullname
      , pp.[product]
      , pp.[status]
      , pp.[deliveryStatus]
   FROM [dbo].[ProductOrder] PO
   JOIN [dbo].[User] U ON U.[id] = PO.[userId] 
    AND PO.[pool] = @userId 
      AND PO.[status] = @status
    AND PO.[deliveryStatus] = ISNULL(@deliveryStatus, PO.[deliveryStatus])

RETURN 0
 

Возможно, SQL server потерян из-за последней строки в предложении where, которая реализует необязательный параметр фильтра?

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

Закрыть эту проблему как ошибку в SQL server?

Комментарии:

1. Это случается ……. Иногда это предполагает дублирование индексов. Вот почему вы не должны слепо следовать его предложениям…. Актуальна ли ваша статистика?

2. Если вы на 100% уверены, что индекс хороший, вы можете использовать подсказки по индексу, чтобы заставить SQL Server использовать этот индекс.

3. Используйте принудительные подсказки по индексу в качестве крайней меры.

4. Итак, как выглядит план выполнения? Он не ищет индекс в этих двух столбцах?

5. Кроме того, в чем именно проблема с текущим планом выполнения? То, что он находится в «топ-10», не означает, что это плохой план. Вы могли бы просто вызывать его миллион раз в час.

Ответ №1:

Ну, какие там индексы? Надеюсь, один userId, status INCLUDE (Id) . Больше ничего не требуется (и любой дополнительный столбец замедлит этот запрос). Если Id значение не равно null или не является частью CI, вам даже не нужно его включать. Вероятно, вы имели в виду и должны писать COUNT(*) в любом случае, потому что вы, вероятно, не хотите не учитывать нули Id .

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

Блокировка, вопреки распространенному суеверию, здесь не является вероятным подозреваемым. На самом деле, NOLOCK это довольно опасно.

Комментарии:

1. Включение идентификатора отсутствовало. Мы попытались добавить идентификатор, но отсутствующий индекс продолжал отображаться (без идентификатора или какого-либо включения вообще). Я изменил количество на *. Я также рассмотрю индексированное представление в качестве решения.

2. Сообщение об отсутствующем индексе — это просто ошибка. Или нет. Если вы опубликуете свое определение индекса, мы сможем убедиться. Если требуется подсчитать слишком много строк или запрос выполняется очень часто, вы ничего не сможете сделать с простым индексом.

3. Я добавил определение индекса. В таблице существует 780 тыс. строк.