Ich habe ein E / A-Problem mit einem großen Tisch.
Allgemeine Statistiken
Die Tabelle weist die folgenden Hauptmerkmale auf:
- Umgebung: Azure SQL-Datenbank (Stufe ist P4 Premium (500 DTUs))
- Zeilen: 2.135.044.521
- 1.275 gebrauchte Partitionen
- Clustered und partitionierter Index
Modell
Dies ist die Tabellenimplementierung:
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
Die Partitionierung hängt damit zusammen:
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', ... )
Servicequalität
Ich denke, die Indizes und Statistiken werden jede Nacht durch schrittweise Neuerstellung / Reorganisation / Aktualisierung gut gepflegt.
Dies sind die aktuellen Indexstatistiken der am häufigsten verwendeten Indexpartitionen:
Dies sind die aktuellen Statistik-Eigenschaften der am häufigsten verwendeten Partitionen:
Problem
Ich führe eine einfache Abfrage mit hoher Frequenz für die Tabelle aus.
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)
Der Ausführungsplan sieht folgendermaßen aus: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG
Mein Problem ist, dass diese Abfragen eine extrem hohe Anzahl von E / A-Operationen erzeugen, was zu einem Engpass bei PAGEIOLATCH_SH
Wartezeiten führt.
Frage
Ich habe gelesen, dass PAGEIOLATCH_SH
Wartezeiten oft mit nicht gut optimierten Indizes zusammenhängen. Gibt es Empfehlungen für mich, wie ich E / A-Vorgänge reduzieren kann? Vielleicht durch Hinzufügen eines besseren Index?
Antwort 1 - bezogen auf Kommentar von @ S4V1N
Der veröffentlichte Abfrageplan stammt aus einer Abfrage, die ich in SSMS ausgeführt habe. Nach Ihrem Kommentar recherchiere ich zum Serververlauf. Die vom Dienst ausgeführte Abfrage sieht etwas anders aus (EntityFramework-bezogen).
(@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)
Auch der Plan sieht anders aus:
https://www.brentozar.com/pastetheplan/?id=H1fhALpKG
oder
https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz
Und wie Sie hier sehen können, wird unsere DB-Leistung von dieser Abfrage kaum beeinflusst.
Antwort 2 - bezogen auf die Antwort von @Joe Obbish
Zum Testen der Lösung habe ich Entity Framework durch einen einfachen SqlCommand ersetzt. Das Ergebnis war eine erstaunliche Leistungssteigerung!
Der Abfrageplan ist jetzt der gleiche wie in SSMS, und die logischen Lese- und Schreibvorgänge fallen auf ~ 8 pro Ausführung.
Die gesamte E / A-Last sinkt auf fast 0!
Es erklärt auch, warum ich einen großen Leistungsabfall bekomme, nachdem ich den Partitionsbereich von monatlich auf täglich geändert habe. Das Fehlen der Partitionseliminierung führte dazu, dass mehr Partitionen gescannt werden mussten.
quelle
Antworten:
Möglicherweise können Sie die
PAGEIOLATCH_SH
Wartezeiten für diese Abfrage reduzieren, wenn Sie die vom ORM generierten Datentypen ändern können. DieTimestamp
Spalte in Ihrer Tabelle hat einen Datentyp von,DATETIME
aber die Parameter@p__linq__1
und@p__linq__2
Datentypen vonDATETIME2(7)
. Dieser Unterschied ist der Grund, warum der Abfrageplan für die ORM-Abfragen so viel komplizierter ist als der erste Abfrageplan, den Sie mit fest codierten Suchfiltern veröffentlicht haben. Einen Hinweis darauf erhalten Sie auch im XML:Mit der ORM-Abfrage können Sie keine Partitionseliminierung erzielen. Sie erhalten mindestens einige logische Lesevorgänge für jede Partition, die in der Partitionsfunktion definiert ist, selbst wenn Sie nur nach einem Tag Daten suchen. Innerhalb jeder Partition erhalten Sie eine Indexsuche, sodass es nicht lange dauert, bis SQL Server zur nächsten Partition übergeht, aber möglicherweise summiert sich das gesamte E / A auf.
Ich habe eine einfache Reproduktion gemacht, um sicher zu sein. Innerhalb der Partitionsfunktion sind 11 Partitionen definiert. Für diese Abfrage:
So sieht IO aus:
Wenn ich die Datentypen korrigiere:
E / A werden durch Partitionseliminierung reduziert:
quelle