Zoptymalizuj indeks w tabeli 2 135 044 521 wierszy

10

Mam problem we / wy z dużym stołem.

Ogólne statystyki

Tabela ma następujące główne cechy:

  • środowisko: Azure SQL Database (warstwa to P4 Premium (500 jednostek DTU))
  • rzędy: 2 135 044 521
  • 1275 używanych partycji
  • indeks klastrowany i podzielony na partycje

Model

Oto implementacja tabeli:

CREATE TABLE [data].[DemoUnitData](
    [UnitID] [bigint] NOT NULL,
    [Timestamp] [datetime] NOT NULL,
    [Value1] [decimal](18, 2) NULL,
    [Value2] [decimal](18, 2) NULL,
    [Value3] [decimal](18, 2) NULL,
    CONSTRAINT [PK_DemoUnitData] PRIMARY KEY CLUSTERED 
    (
        [UnitID] ASC,
        [Timestamp] ASC
    )
)
GO

ALTER TABLE [data].[DemoUnitData] WITH NOCHECK ADD CONSTRAINT [FK_DemoUnitData_Unit] FOREIGN KEY([UnitID])
REFERENCES [model].[Unit] ([ID])
GO

ALTER TABLE [data].[DemoUnitData] CHECK CONSTRAINT [FK_DemoUnitData_Unit]
GO

Partycjonowanie jest z tym związane:

CREATE PARTITION SCHEME [DailyPartitionSchema] AS PARTITION [DailyPartitionFunction] ALL TO ([PRIMARY])

CREATE PARTITION FUNCTION [DailyPartitionFunction] (datetime) AS RANGE RIGHT
FOR VALUES (N'2017-07-25T00:00:00.000', N'2017-07-26T00:00:00.000', N'2017-07-27T00:00:00.000', ... )

Jakość usługi

Myślę, że indeksy i statystyki są dobrze utrzymywane każdej nocy dzięki stopniowej przebudowie / reorganizacji / aktualizacji.

Oto bieżące statystyki indeksów najczęściej używanych partycji indeksowych:

Statystyki partycji

Oto bieżące właściwości statystyczne najczęściej używanych partycji:

Statystyka

Problem

Wykonuję proste zapytanie o wysoką częstotliwość względem tabeli.

SELECT [UnitID]
    ,[Timestamp]
    ,[Value1]
    ,[Value2]
    ,[Value3]
FROM [data].[DemoUnitData]
WHERE [UnitID] = 8877 AND [Timestamp] >= '2018-03-01' AND [Timestamp] < '2018-03-13'
OPTION (MAXDOP 1)

liczba exce

Plan wykonania wygląda następująco: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG

Mój problem polega na tym, że te zapytania powodują wyjątkowo dużą liczbę operacji we / wy, co powoduje wąskie gardło PAGEIOLATCH_SHoczekiwania.

top czeka

Pytanie

Czytałem, że PAGEIOLATCH_SHoczekiwania są często związane z niezbyt zoptymalizowanymi indeksami. Czy masz jakieś zalecenia dotyczące ograniczenia operacji we / wy? Może dodając lepszy indeks?


Odpowiedź 1 - związana z komentarzem @ S4V1N

Wysłany plan zapytań pochodzi z zapytania, które wykonałem w SSMS. Po twoim komentarzu przeprowadzam badania historii serwera. Aktualne zapytanie pobrane z usługi wygląda nieco inaczej (związane z EntityFramework).

(@p__linq__0 bigint,@p__linq__1 datetime2(7),@p__linq__2 datetime2(7)) 

SELECT 1 AS [C1], [Extent1] 
   .[Timestamp] AS [Timestamp], [Extent1] 
   .[Value1] AS [Value1], [Extent1] 
   .[Value2] AS [Value2], [Extent1] 
   .[Value3] AS [Value3]  
FROM [data].[DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2) OPTION (MAXDOP 1) 

Ponadto plan wygląda inaczej:

https://www.brentozar.com/pastetheplan/?id=H1fhALpKG

lub

https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz

I jak widać tutaj, to zapytanie nie ma wpływu na naszą wydajność DB.

Top SQL

Odpowiedź 2 - związana z odpowiedzią @Joe Obbish

W celu przetestowania rozwiązania zastąpiłem Entity Framework prostym SqlCommand. Rezultatem był niesamowity wzrost wydajności!

Plan zapytań jest teraz taki sam jak w SSMS, a logiczne odczyty i zapisy spadają do ~ 8 na wykonanie.

Całkowite obciążenie we / wy spada do prawie 0! Spadek we / wy

Wyjaśnia również, dlaczego mam duży spadek wydajności po zmianie zakresu partycji z miesięcznego na dzienny. Brak eliminacji partycji spowodował, że więcej partycji do skanowania.

Steffen Mangold
źródło
2
Patrząc na plan wykonania, zapytanie to wcale nie wydaje się problematyczne, skanowało tylko niezbędne partycje z małą ilością odczytów i nie zgłosiło zamiast tego stronyiolatch_sh czeka (sos_sched ..). Co jest zrozumiałe, ponieważ i tak nie miałeś fizycznych odczytów. Czy te skumulowane oczekiwania lub zajęte przez określony czas? Być może problemem jest jednak jakieś inne zapytanie.
S4V1N,
Powyżej zamieściłem szczegółową odpowiedź @ S4V1N
Steffen Mangold

Odpowiedzi:

7

Możliwe, że będziesz w stanie skrócić czas PAGEIOLATCH_SHoczekiwania na to zapytanie, jeśli będziesz w stanie zmienić typy danych generowane przez ORM. TimestampKolumna w tabeli ma typ danych DATETIMEale parametry @p__linq__1i @p__linq__2mieć typy danych DATETIME2(7). Różnica polega na tym, że plan zapytań dla zapytań ORM jest o wiele bardziej skomplikowany niż pierwszy opublikowany plan zapytań z zakodowanymi filtrami wyszukiwania. Można to również znaleźć w pliku XML:

<ScalarOperator ScalarString="GetRangeWithMismatchedTypes([@p__linq__1],NULL,(22))">

Podobnie jak w przypadku zapytania ORM nie można uzyskać żadnej eliminacji partycji. Otrzymasz co najmniej kilka logicznych odczytów dla każdej partycji zdefiniowanej w funkcji partycji, nawet jeśli szukasz tylko jednego dnia danych. W obrębie każdej partycji pojawia się wyszukiwanie indeksu, aby SQL Server nie przeszedł do następnej partycji, ale być może całość tego IO się sumuje.

Dla pewności wykonałem prostą reprodukcję. W funkcji partycji zdefiniowano 11 stref. Dla tego zapytania:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime2(7) = '20180103';
DECLARE @p__linq__2 datetime2(7) = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

Oto jak wygląda IO:

Tabela „DemoUnitData”. Liczba skanów 11, logiczne odczyty 40

Kiedy naprawiam typy danych:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime = '20180103';
DECLARE @p__linq__2 datetime = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

IO jest zmniejszone w wyniku eliminacji partycji:

Tabela „DemoUnitData”. Liczba skanów 2, logiczne odczyty 8

Joe Obbish
źródło