Eu tenho um problema de E / S com uma tabela grande.
Estatísticas gerais
A tabela possui as seguintes características principais:
- ambiente: Banco de Dados SQL do Azure (a camada é P4 Premium (500 DTUs))
- linhas: 2.135.044.521
- 1.275 partições usadas
- índice em cluster e particionado
Modelo
Esta é a implementação da tabela:
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
O particionamento está relacionado a isso:
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', ... )
Qualidade de serviço
Eu acho que os índices e as estatísticas são bem mantidos todas as noites por reconstrução / reorganização / atualização incremental.
Estas são as estatísticas atuais do índice das partições de índice mais usadas:
Estas são as propriedades estatísticas atuais das partições mais usadas:
Problema
Eu executo uma consulta simples em alta frequência na tabela.
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)
O plano de execução é assim: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG
Meu problema é que essas consultas produzem uma quantidade extremamente alta de operações de E / S, resultando em um gargalo de PAGEIOLATCH_SH
esperas.
Questão
Li que as PAGEIOLATCH_SH
esperas geralmente estão relacionadas a índices não otimizados. Há alguma recomendação que você tenha para mim sobre como reduzir as operações de E / S? Talvez adicionando um índice melhor?
Resposta 1 - relacionada ao comentário de @ S4V1N
O plano de consulta publicado era de uma consulta que eu executei no SSMS. Após o seu comentário, faço algumas pesquisas sobre o histórico do servidor. A consulta acumulada excedida do serviço parece um pouco diferente (relacionada ao 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)
Além disso, o plano parece diferente:
https://www.brentozar.com/pastetheplan/?id=H1fhALpKG
ou
https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz
E, como você pode ver aqui, nosso desempenho do banco de dados dificilmente é influenciado por essa consulta.
Resposta 2 - relacionada à resposta de @Joe Obbish
Para testar a solução, substituí o Entity Framework por um SqlCommand simples. O resultado foi um incrível aumento de desempenho!
O plano de consulta agora é o mesmo do SSMS e as leituras e gravações lógicas caem para ~ 8 por execução.
A carga geral de E / S cai para quase 0!
Ele também explica por que recebo uma grande queda no desempenho depois que alterei o intervalo de partições de mensal para diário. A falta de eliminação de partições resultou em mais partições a serem verificadas.
fonte
Respostas:
Você poderá reduzir as
PAGEIOLATCH_SH
esperas por essa consulta se conseguir alterar os tipos de dados gerados pelo ORM. ATimestamp
coluna na sua tabela tem um tipo de dadosDATETIME
mas os parâmetros@p__linq__1
e@p__linq__2
tem tipos de dadosDATETIME2(7)
. Essa diferença é a razão pela qual o plano de consulta para as consultas ORM é muito mais complicado que o primeiro plano de consulta que você postou com filtros de pesquisa codificados. Você também pode obter uma dica disso no XML:Como é, com a consulta ORM, você não pode obter nenhuma eliminação de partição. Você receberá pelo menos algumas leituras lógicas para cada partição definida na função de partição, mesmo se estiver apenas procurando um dia de dados. Em cada partição, você obtém uma pesquisa de índice, para que o SQL Server não demore muito para passar para a próxima partição, mas talvez todo esse IO esteja aumentando.
Fiz uma reprodução simples para ter certeza. Existem 11 partições definidas dentro da função de partição. Para esta consulta:
Eis a aparência do IO:
Quando eu ajusto os tipos de dados:
O IO é reduzido como resultado da eliminação da partição:
fonte