Как диагностировать медленный / противоречивый запрос SQL Server? - PullRequest
0 голосов
/ 23 февраля 2019

Под управлением Windows Server 2012, Hyper-V, SQL Server 2012 активный / пассивный отказоустойчивый кластер с двумя 8-процессорными узлами, 60 ГБ, один экземпляр, 300 баз данных.Этот запрос дает противоречивые результаты, выполняемые где-то между 10 и 30 секундами.

DECLARE @OrgID           BigInt = 780246
DECLARE @ActiveOnly      Bit = 0
DECLARE @RestrictToOrgID Bit = 0;

WITH og (OrgID, GroupID) AS
  (
  SELECT ID, ID FROM Common.com.Organizations WHERE ISNULL(ParentID, 0) <> ID 
  UNION ALL
  SELECT o.ID, og.GroupID FROM Common.com.Organizations o JOIN og ON og.OrgID = o.ParentID
  )

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN books.Entities                e  ON   e.OrgID    = po.ID
     JOIN Vendors                       v  ON   v.ID       =  e.ID 
                                           AND (e.OrgID    = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

Замена LEFT JOIN Addresses на JOIN Addresses

     JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

или уменьшение длины столбцов, выбранных изAddresses до менее 100 байтов

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.Fax

сокращает время выполнения примерно до 0,5 секунд.

Кроме того, использование SELECT DISTINCT и соединение books.Entities с Vendors

   SELECT DISTINCT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN Vendors                       v  
     JOIN books.Entities                e  ON   v.ID       =  e.ID 
                                           ON   e.OrgID    = bo.ID OR (e.OrgID = po.ID AND v.DistrictWide = 1)

Сокращает время примерно до 0,75 секунд.

Резюме

Эти условия указывают на то, что в экземпляре SQL Server есть какое-то ограничение ресурса, которое приводит к таким ошибочным результатам, и я не знаю, как его диагностировать.Если я скопирую поврежденную базу данных на свой ноутбук с SQL Server 2012, проблема не возникает.Я могу продолжать менять SQL и надеяться на лучшее, но я бы предпочел найти более определенное решение.

Любые предложения приветствуются.

Обновление 2/27/18

В плане выполнения для неизмененного запроса в качестве проблемы показан поиск кластерного индекса по таблице адресов.Clustered Index Seek against the Addresses table

Уменьшение длины выбранных столбцов с Addresses до менее 100 байтов

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.Fax

заменил поиск кластеризованного индекса сканированием кластеризованного индексачтобы получить a.Fax и Hash Match для присоединения этого значения к результатам.

Hash Match

Первичный ключ таблицы Addresses создается следующим образом:

   ALTER TABLE dbo.Addresses 
ADD CONSTRAINT PK_Addresses PRIMARY KEY CLUSTERED (ID ASC)
          WITH (PAD_INDEX = OFF, 
                STATISTICS_NORECOMPUTE = OFF, 
                SORT_IN_TEMPDB         = OFF, 
                IGNORE_DUP_KEY         = OFF, 
                ONLINE                 = OFF, 
                ALLOW_ROW_LOCKS        = ON, 
                ALLOW_PAGE_LOCKS       = ON) 
            ON  PRIMARY

Этот индекс дефрагментируется и оптимизируется по мере необходимости каждый день.

Пока что я не могу найти ничего полезного в том, почему поиск по кластерному индексу так много времени добавляет к запросу..

1 Ответ

0 голосов
/ 07 марта 2019

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

Первичной проблемой оказался рекурсивный CTE og, который возвращает сводную таблицу, дающую родительские / дочерние отношения между организациями.Однако анализ планов выполнения, по-видимому, указывает на то, что проблема была в некотором сбое в оптимизаторе, связанном с объемом данных, возвращаемых из левой таблицы.Это может быть полностью результатом моей неспособности должным образом проанализировать план выполнения, но, похоже, есть некоторая проблема в том, как SQL Server 2012 SP4 создает план выполнения в этих обстоятельствах.

Хотя это гораздо более важно для нашей работыНа сервере, проблема с оптимизацией рекурсивного CTE для SQL Server была очевидна как на моем локальном хосте, работающем под управлением 2012 SP4, так и на промежуточном сервере, работающем под управлением SP2.Но потребовался дальнейший анализ и некоторые догадки, чтобы увидеть его.

Решение

Я заменил рекурсивный CTE на сводную таблицу и добавил триггер в таблицу «Организации» дляподдержите его.

USE Common
GO

CREATE VIEW com.OrganizationGroupsCTE
AS
  WITH cte (OrgID, GroupID) AS
    (
    SELECT ID, ID FROM com.Organizations WHERE ISNULL(ParentID, 0) <> ID 
    UNION ALL
    SELECT o.ID, cte.GroupID FROM com.Organizations o JOIN cte ON cte.OrgID = o.ParentID
    )

  SELECT OrgID, GroupID FROM cte
GO

CREATE TABLE com.OrganizationGroups 
  (
  OrgID   BIGINT, 
  GroupID BIGINT
  )

INSERT com.OrganizationGroups 
SELECT OrgID, GroupID 
  FROM com.OrganizationGroupsCTE
GO

CREATE TRIGGER TR_OrganizationGroups ON com.Organizations AFTER INSERT,UPDATE,DELETE
AS
  DELETE og 
    FROM com.OrganizationGroups og 
    JOIN deleted                d   ON d.ID IN (og.groupID, og.orgID);

  INSERT com.OrganizationGroups 
  SELECT orgID, groupID 
    FROM inserted               i 
    JOIN OrganizationGroupsCTE  cte ON i.ID IN (cte.orgID, cte.groupID)
GO

После изменения запроса для использования сводной таблицы

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM Common.com.OrganizationGroups og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN books.Entities                e  ON   e.OrgID    = po.ID
     JOIN Vendors                       v  ON   v.ID       =  e.ID 
                                           AND (e.OrgID    = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

Производительность SQL Server была улучшена и согласована во всех трех средах.Проблемы на рабочем сервере теперь устранены.

SQL Server Performance

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