Скорость изменения запросов (34 против 0 секунд), без изменения данных / схемы без кэша плана выполнения (генерируется SQL из EF Core) - PullRequest
0 голосов
/ 20 января 2020

У меня довольно большой запрос, который выполняется очень и очень медленно на БД с очень небольшим объемом данных (наибольшее количество строк для таблицы, к которой этот запрос обращается, составляет 8 КБ, и большинство фильтров относятся к идентификаторам со свойствами навигации EF, передан параметр - один единственный идентификатор).

Он шел гладко, но после нескольких изменений (к сожалению, сегодня мы изменили «лот», чтобы преобразовать его в один запрос, пока он не превысил 30 в cs html с отложенной загрузкой и теперь все предварительно загружено в начале файла) теперь он работает в темпе (время ожидания составляет 30 с c и занимает 34). Запуск его в SQL серверной студии управления обеспечивает одно и то же время выполнения (34 се c), выполнение этого несколько раз не ускоряет его, показывает план выполнения, немного ждет, затем запускает его снова и в какой-то момент идет от 34 до 0 секунд.

Мы исключили следующее:

  • Кэширование выполнения плана запроса ( Как очистить запрос SQL к серверу кэш? ): время выполнения не меняется после сброса планов, как только оно быстро, оно остается быстрым

  • Результат не кэшируется: изменение guid (которое изменяет значения всего остального) ничего не меняет, раз оно быстро, оно остается быстрым

  • SQL Проблема с установкой сервера: когда это случилось на моем коллеге P C, я взял последнюю версия и воспроизводит ту же проблему

  • Проблемы с компьютером: ничего не занимает процессорное время, кроме сервера SQL, больше ничего не открывается, оба компьютера работают нормально и не связаны (не включены тот же ne twork / not on domain et c), что-то, что могло бы повлиять на них обоих отрицательно в одно и то же время, может быть исключено.

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

Я не уверен, что этот запрос настолько полезен, поэтому, почему я публикую его в последний раз, я переименовал некоторые таблицы (из T0 T1 и др. c) чтобы было легче читать, но это не так легко переварить. Ниже вы найдете и код C#, использованный для его генерации, и результат SQL, который снова и снова занимает 34 секунды, пока не потребуется 0!

LINQ (ef core latest, 3.1):

Context.Orders
    .Where(o => o.UniqueId == new Guid("MY GUID HERE"))
    .SelectMany(o => o.Products)
    .Where(FranceMontgolfieres.Models.Mappings.ProductOrder.IsTicketExpression)
    .Select(po => new
    {
        po.UniqueId,
        ProductId = po.Product.Id,
        ProductName = po.Product.Name,
        AllPassengerAreRegistered = po.Passengers.All(pa => pa.Passenger != null),
        passengers = po.Passengers
                .Select(pa => new
                {
                    pa.UniqueId,
                    IsRegistered = pa.Passenger != null,
                    pa.Passenger.FirstName,
                    pa.Passenger.LastName,
                    FullName = pa.Passenger.FirstName + " " + pa.Passenger.LastName,
                    HasFlown = pa.Passenger.Manifests
                    .Any(m => m.History
                        .Any(h => h.Status == PassengerManifestStatus.Flown)),
                    HasPendingFlight = pa.Passenger.Manifests
                    .Any(m => m.History
                        .Any(h => h.Status == PassengerManifestStatus.Incoming)),
                    HasPendingReport = pa.Passenger.Manifests
                    .Any(m => m.History
                        .Any(h => h.Status == PassengerManifestStatus.Report)),
                    Manifests = pa.Passenger.Manifests
                        .Select(m => new
                        {
                            manifest = new
                            {
                                HasFlown = m.History
                                    .Any(h => h.Status == PassengerManifestStatus.Flown),
                                HasPendingFlight = m.History
                                    .Any(h => h.Status == PassengerManifestStatus.Incoming),
                                HasPendingReport = m.History
                                    .Any(h => h.Status == PassengerManifestStatus.Report),
                                m.Manifest.StartDate,
                                m.Manifest.FlightBase.Name,
                                m.Manifest.FlightBase.Location,
                                m.Manifest.State
                            },
                            history = m.History
                                .Select(h => new
                                {
                                    h.Date,
                                    h.Status
                                })
                        })
                }),
        PendingReservationExists = po.PendingReservation != null
    })
    .ToList();

Что мы запускаем перед запросом (по-прежнему занимает 0 se c после), чтобы убедиться, что он не кэшируется:

CHECKPOINT; 
GO 
DBCC DROPCLEANBUFFERS; 
GO
DBCC FREEPROCCACHE;
GO
DBCC FREESYSTEMCACHE("ALL");
GO
DBCC FREESESSIONCACHE;
GO
DBCC FLUSHAUTHCACHE;
GO

SQL Запрос:

SELECT [ProductOrders_p1].[UniqueId],
    [Products_p2].[Id], 
    [TranslatableString_t].[Id], 
    [TranslatableString_t].[MainPageCompanyDetailId], 
    [TranslatableString_t].[Text], 
    CASE
          WHEN NOT EXISTS (
              SELECT 1
              FROM [ProductOrderPassenger] AS [p]
              LEFT JOIN [Passengers] AS [p0] ON [p].[Id] = [p0].[ProductOrderPassengerId]
              WHERE ([ProductOrders_p1].[Id] = [p].[ProductOrderId]) AND [p0].[Id] IS NULL) THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
      END,
    CASE
          WHEN [PendingReservations_p3].[Id] IS NOT NULL THEN CAST(1 AS bit)
          ELSE CAST(0 AS bit)
      END, 
    [o].[Id], 
    [ProductOrders_p1].[Id], 
    [t3].[UniqueId], 
    [t3].[c], 
    [t3].[FirstName], 
    [t3].[LastName], 
    [t3].[c0], 
    [t3].[c1], 
    [t3].[c2],
    [t3].[c3], 
    [t3].[Id], 
    [t3].[c4], 
    [t3].[c00], 
    [t3].[c10],
    [t3].[StartDate], 
    [t3].[Id0], 
    [t3].[MainPageCompanyDetailId], 
    [t3].[Text], 
    [t3].[Id00], 
    [t3].[MainPageCompanyDetailId0], 
    [t3].[Text0], 
    [t3].[State], 
    [t3].[ManifestId], 
    [t3].[PassengerId], 
    [t3].[Id1], 
    [t3].[Date],
    [t3].[Status], 
    [t3].[Id2]
      FROM [Orders] AS [o]
      INNER JOIN [ProductOrders] AS [ProductOrders_p1] ON [o].[Id] = [ProductOrders_p1].[OrderId]
      LEFT JOIN [Products] AS [Products_p2] ON [ProductOrders_p1].[ProductId] = [Products_p2].[Id]
      LEFT JOIN [TranslatableStrings] AS [TranslatableString_t] ON [Products_p2].[NameId] = [TranslatableString_t].[Id]
      LEFT JOIN [PendingReservations] AS [PendingReservations_p3] ON [ProductOrders_p1].[Id] = [PendingReservations_p3].[ProductOrderId]
      LEFT JOIN (
          SELECT [ProductOrderPassengers_p4].[UniqueId], CASE
              WHEN [Passengers_p5].[Id] IS NOT NULL THEN CAST(1 AS bit)
              ELSE CAST(0 AS bit)
          END AS [c], [Passengers_p5].[FirstName], [Passengers_p5].[LastName], ([Passengers_p5].[FirstName] + N' ') + [Passengers_p5].[LastName] AS [c0], CASE
              WHEN EXISTS (
                  SELECT 1
                  FROM [ManifestPassengers] AS [m]
                  WHERE ([Passengers_p5].[Id] IS NOT NULL AND ([Passengers_p5].[Id] = [m].[PassengerId])) AND EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m0]
                      WHERE (([m].[ManifestId] = [m0].[ManifestPassengerManifestId]) AND ([m].[PassengerId] = [m0].[ManifestPassengerPassengerId])) AND ([m0].[Status] = 5))) THEN CAST(1 AS bit)
              ELSE CAST(0 AS bit)
          END AS [c1], CASE
              WHEN EXISTS (
                  SELECT 1
                  FROM [ManifestPassengers] AS [m1]
                  WHERE ([Passengers_p5].[Id] IS NOT NULL AND ([Passengers_p5].[Id] = [m1].[PassengerId])) AND EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m2]
                      WHERE (([m1].[ManifestId] = [m2].[ManifestPassengerManifestId]) AND ([m1].[PassengerId] = [m2].[ManifestPassengerPassengerId])) AND ([m2].[Status] = 1))) THEN CAST(1 AS bit)
              ELSE CAST(0 AS bit)
          END AS [c2], CASE
              WHEN EXISTS (
                  SELECT 1
                  FROM [ManifestPassengers] AS [m3]
                  WHERE ([Passengers_p5].[Id] IS NOT NULL AND ([Passengers_p5].[Id] = [m3].[PassengerId])) AND EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m4]
                      WHERE (([m3].[ManifestId] = [m4].[ManifestPassengerManifestId]) AND ([m3].[PassengerId] = [m4].[ManifestPassengerPassengerId])) AND ([m4].[Status] = 2))) THEN CAST(1 AS bit)
              ELSE CAST(0 AS bit)
          END AS [c3], [ProductOrderPassengers_p4].[Id], [t2].[c] AS [c4], [t2].[c0] AS [c00], [t2].[c1] AS [c10], [t2].[StartDate], [t2].[Id] AS [Id0], [t2].[MainPageCompanyDetailId], [t2].[Text], [t2].[Id0] AS [Id00], [t2].[MainPageCompanyDetailId0], [t2].[Text0], [t2].[State], [t2].[ManifestId], [t2].[PassengerId], [t2].[Id1], [t2].[Date], [t2].[Status], [t2].[Id2], [ProductOrderPassengers_p4].[ProductOrderId]
          FROM [ProductOrderPassenger] AS [ProductOrderPassengers_p4]
          LEFT JOIN [Passengers] AS [Passengers_p5] ON [ProductOrderPassengers_p4].[Id] = [Passengers_p5].[ProductOrderPassengerId]
          LEFT JOIN (
              SELECT CASE
                  WHEN EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m5]
                      WHERE (([m8].[ManifestId] = [m5].[ManifestPassengerManifestId]) AND ([m8].[PassengerId] = [m5].[ManifestPassengerPassengerId])) AND ([m5].[Status] = 5)) THEN CAST(1 AS bit)
                  ELSE CAST(0 AS bit)
              END AS [c], CASE
                  WHEN EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m6]
                      WHERE (([m8].[ManifestId] = [m6].[ManifestPassengerManifestId]) AND ([m8].[PassengerId] = [m6].[ManifestPassengerPassengerId])) AND ([m6].[Status] = 1)) THEN CAST(1 AS bit)
                  ELSE CAST(0 AS bit)
              END AS [c0], CASE
                  WHEN EXISTS (
                      SELECT 1
                      FROM [ManifestPassengerHistories] AS [m7]
                      WHERE (([m8].[ManifestId] = [m7].[ManifestPassengerManifestId]) AND ([m8].[PassengerId] = [m7].[ManifestPassengerPassengerId])) AND ([m7].[Status] = 2)) THEN CAST(1 AS bit)
                  ELSE CAST(0 AS bit)
              END AS [c1], [m9].[StartDate], [t0].[Id], [t0].[MainPageCompanyDetailId], [t0].[Text], [t1].[Id] AS [Id0], [t1].[MainPageCompanyDetailId] AS [MainPageCompanyDetailId0], [t1].[Text] AS [Text0], [m9].[State], [m8].[ManifestId], [m8].[PassengerId], [m9].[Id] AS [Id1], [m10].[Date], [m10].[Status], [m10].[Id] AS [Id2]
              FROM [ManifestPassengers] AS [m8]
              INNER JOIN [Manifests] AS [m9] ON [m8].[ManifestId] = [m9].[Id]
              LEFT JOIN [FlightBases] AS [f] ON [m9].[FlightBaseId] = [f].[Id]
              LEFT JOIN [TranslatableStrings] AS [t0] ON [f].[NameId] = [t0].[Id]
              LEFT JOIN [TranslatableStrings] AS [t1] ON [f].[LocationId] = [t1].[Id]
              LEFT JOIN [ManifestPassengerHistories] AS [m10] ON ([m8].[ManifestId] = [m10].[ManifestPassengerManifestId]) AND ([m8].[PassengerId] = [m10].[ManifestPassengerPassengerId])
          ) AS [t2] ON [Passengers_p5].[Id] = [t2].[PassengerId]
      ) AS [t3] ON [ProductOrders_p1].[Id] = [t3].[ProductOrderId]
      WHERE ([o].[UniqueId] = 'd26013b9-b9bc-4497-b351-544739851d3d') AND EXISTS (
          SELECT 1
          FROM [ProductProductComponents] AS [p6]
          INNER JOIN (
              SELECT [p7].[Id], [p7].[DescriptionId], [p7].[Discriminator], [p7].[DisplayDescriptionInProduct], [p7].[FunctionnalName], [p7].[HTPrice], [p7].[IsArchived], [p7].[IsChildrenPrice], [p7].[IsOptionnal], [p7].[IsSelectedInBasketByDefault], [p7].[IsShipping], [p7].[NameId], [p7].[ParentProductComponentId], [p7].[ShowInBasket], [p7].[ShowInInvoice], [p7].[VATRate], [p7].[AllowPartnerBase], [p7].[AllowWeekends], [p7].[ValidForTicks]
              FROM [ProductComponents] AS [p7]
              WHERE [p7].[Discriminator] IN (N'ProductComponent', N'FlightProductComponent')
          ) AS [t4] ON [p6].[ProductComponentId] = [t4].[Id]
          WHERE ([Products_p2].[Id] IS NOT NULL AND ([Products_p2].[Id] = [p6].[ProductId])) AND ([t4].[Discriminator] = N'FlightProductComponent'))
      ORDER BY [o].[Id], [ProductOrders_p1].[Id], [t3].[Id], [t3].[ManifestId], [t3].[PassengerId], [t3].[Id1], [t3].[Id2]

1 Ответ

0 голосов
/ 06 февраля 2020

Можно ли рассмотреть вопрос о помещении запроса в хранимую процедуру? Это должно помочь сохранить тот же план, который вы вставляете в новые значения параметра. В качестве альтернативы вы можете попытаться обновить статистику sh и проверить фрагментацию индекса по уникальному идентификатору. Это может привести к плохому плану, получая плохие оценки.

Если бы мне нужно было сделать предположение, основными точками индекса являются «Заказ», «Производитель», «Пассажиры», а с левым соединением между «Пассажирами» и «ProductOrderPassengers» происходит нечто странное, если порядок таблиц переворачивается. Трюк, который можно попробовать, если вы должны остаться ad ho c, это что-то вроде

Объявление @Oid varchar (10) Set @Oid = Выбрать ID из заказа, где UniqueID = 'd26013b9-b9b c -4497 -b351-544739851d3d '

, затем добавьте предложение where для этого идентификатора, если для оценки существует 1 строка.

Если это как 1 уникальный идентификатор для многих идентификаторов, вы можете перетащить их в временная таблица, а затем присоединиться к ней. Я знаю, что такие вещи помогли укротить наши неверные оценки, но обычно мы пытаемся использовать только простые запросы для ad ho c, сложные go в хранимых процессах.

Я не думаю, что очистка вашего кэша оказывает вам какие-либо услуги.

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