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:
Oto bieżące właściwości statystyczne najczęściej używanych partycji:
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)
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_SH
oczekiwania.
Pytanie
Czytałem, że PAGEIOLATCH_SH
oczekiwania 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.
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!
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.
źródło
Odpowiedzi:
Możliwe, że będziesz w stanie skrócić czas
PAGEIOLATCH_SH
oczekiwania na to zapytanie, jeśli będziesz w stanie zmienić typy danych generowane przez ORM.Timestamp
Kolumna w tabeli ma typ danychDATETIME
ale parametry@p__linq__1
i@p__linq__2
mieć typy danychDATETIME2(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: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:
Oto jak wygląda IO:
Kiedy naprawiam typy danych:
IO jest zmniejszone w wyniku eliminacji partycji:
źródło