Tengo un problema de E / S con una mesa grande.
Estadísticas generales
La tabla tiene las siguientes características principales:
- entorno: Azure SQL Database (el nivel es P4 Premium (500 DTU))
- filas: 2,135,044,521
- 1,275 particiones usadas
- índice agrupado y particionado
Modelo
Esta es la implementación de la tabla:
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
La partición está relacionada con esto:
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', ... )
Calidad de servicio
Creo que los índices y las estadísticas se mantienen bien todas las noches mediante la reconstrucción / reorganización / actualización incremental.
Estas son las estadísticas de índice actuales de las particiones de índice más utilizadas:
Estas son las propiedades estadísticas actuales de las particiones más utilizadas:
Problema
Ejecuto una consulta simple en una alta frecuencia contra la tabla.
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)
El plan de ejecución se ve así: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG
Mi problema es que estas consultas producen una cantidad extremadamente alta de operaciones de E / S que resultan en un cuello de botella de PAGEIOLATCH_SH
esperas.
Pregunta
He leído que las PAGEIOLATCH_SH
esperas a menudo están relacionadas con índices no bien optimizados. ¿Hay alguna recomendación que tenga para mí sobre cómo reducir las operaciones de E / S? ¿Quizás agregando un mejor índice?
Respuesta 1 - relacionada con el comentario de @ S4V1N
El plan de consulta publicado fue de una consulta que ejecuté en SSMS. Después de tu comentario, investigo un poco sobre el historial del servidor. La consulta accidental superada por el servicio se ve un poco diferente (EntityFramework relacionado).
(@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)
Además, el plan se ve diferente:
https://www.brentozar.com/pastetheplan/?id=H1fhALpKG
o
https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz
Y como puede ver aquí, nuestro rendimiento de la base de datos apenas está influenciado por esta consulta.
Respuesta 2 - relacionada con la respuesta de @Joe Obbish
Para probar la solución, reemplacé Entity Framework con un simple SqlCommand. ¡El resultado fue un increíble aumento de rendimiento!
El plan de consulta ahora es el mismo que en SSMS y las lecturas y escrituras lógicas caen a ~ 8 por ejecución.
¡La carga general de E / S cae a casi 0!
También explica por qué obtengo una gran caída de rendimiento después de cambiar el rango de partición de mensual a diario. La falta de eliminación de la partición resultó en más particiones para escanear.
fuente
Respuestas:
Es posible que pueda reducir las
PAGEIOLATCH_SH
esperas para esta consulta si puede cambiar los tipos de datos generados por el ORM. LaTimestamp
columna en su tabla tiene un tipo de datos deDATETIME
pero los parámetros@p__linq__1
y@p__linq__2
tienen tipos de datos deDATETIME2(7)
. Esa diferencia es la razón por la cual el plan de consulta para las consultas ORM es mucho más complicado que el primer plan de consulta que publicó que tenía filtros de búsqueda codificados. También puede obtener una pista de esto en el XML:Tal como está, con la consulta ORM no se puede eliminar ninguna partición. Obtendrá al menos algunas lecturas lógicas para cada partición definida en la función de partición, incluso si solo está buscando un día de datos. Dentro de cada partición, obtiene una búsqueda de índice, por lo que SQL Server no tarda mucho en pasar a la siguiente partición, pero quizás todo ese IO se está acumulando.
Hice una reproducción simple para estar seguro. Hay 11 particiones definidas dentro de la función de partición. Para esta consulta:
Así es como se ve IO:
Cuando reparo los tipos de datos:
IO se reduce como resultado de la eliminación de la partición:
fuente