J'ai un problème d'E / S avec une grande table.
Statistiques générales
Le tableau présente les principales caractéristiques suivantes:
- environnement: Azure SQL Database (le niveau est P4 Premium (500 DTU))
- rangées: 2,135,044,521
- 1275 partitions utilisées
- index clusterisé et partitionné
Modèle
Voici l'implémentation de la table:
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
Le partitionnement est lié à ceci:
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', ... )
Qualité de service
Je pense que les index et les statistiques sont bien entretenus chaque nuit par une reconstruction / réorganisation / mise à jour incrémentielle.
Voici les statistiques d'index actuelles des partitions d'index les plus utilisées:
Voici les propriétés statistiques actuelles des partitions les plus utilisées:
Problème
J'exécute une simple requête sur une haute fréquence contre la table.
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)
Le plan d'exécution ressemble à ceci: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG
Mon problème est que ces requêtes produisent une quantité extrêmement élevée d'opérations d'E / S entraînant un goulot d'étranglement des PAGEIOLATCH_SH
attentes.
Question
J'ai lu que les PAGEIOLATCH_SH
attentes sont souvent liées à des index mal optimisés. Avez-vous des recommandations à me faire pour réduire les opérations d'E / S? Peut-être en ajoutant un meilleur index?
Réponse 1 - liée au commentaire de @ S4V1N
Le plan de requête publié provenait d'une requête que j'ai exécutée dans SSMS. Après votre commentaire, je fais quelques recherches sur l'historique du serveur. La requête accual extraite du service est un peu différente (liée à 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)
De plus, le plan est différent:
https://www.brentozar.com/pastetheplan/?id=H1fhALpKG
ou
https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz
Et comme vous pouvez le voir ici, nos performances DB ne sont guère influencées par cette requête.
Réponse 2 - liée à la réponse de @Joe Obbish
Pour tester la solution, j'ai remplacé Entity Framework par un simple SqlCommand. Le résultat a été une incroyable amélioration des performances!
Le plan de requête est maintenant le même que dans SSMS et les lectures et écritures logiques tombent à ~ 8 par exécution.
La charge globale d'E / S chute à presque 0!
Cela explique également pourquoi j'obtiens une baisse importante des performances après avoir changé la plage de partition de mensuel à quotidien. L'absence d'élimination de partition a entraîné davantage de partitions à analyser.
la source
Réponses:
Vous pourrez peut-être réduire les temps d'
PAGEIOLATCH_SH
attente pour cette requête si vous pouvez modifier les types de données générés par l'ORM. LaTimestamp
colonne de votre table a un type de donnéesDATETIME
mais les paramètres@p__linq__1
et@p__linq__2
des types de donnéesDATETIME2(7)
. Cette différence explique pourquoi le plan de requête pour les requêtes ORM est tellement plus compliqué que le premier plan de requête que vous avez publié qui avait des filtres de recherche codés en dur. Vous pouvez également obtenir un indice de ceci dans le XML:En l'état, avec la requête ORM, vous ne pouvez obtenir aucune élimination de partition. Vous obtiendrez au moins quelques lectures logiques pour chaque partition définie dans la fonction de partition, même si vous recherchez simplement une journée de données. Dans chaque partition, vous obtenez une recherche d'index, il ne faut donc pas longtemps à SQL Server pour passer à la partition suivante, mais peut-être que toute cette entrée-sortie s'accumule.
J'ai fait une simple reproduction pour être sûr. Il y a 11 partitions définies dans la fonction de partition. Pour cette requête:
Voici à quoi ressemble IO:
Lorsque je corrige les types de données:
IO est réduit en raison de l'élimination de la partition:
la source