Ottimizza l'indice su una tabella a 2.135.044.521 righe


10

Ho un problema I / O con una tabella di grandi dimensioni.

Statistiche generali

La tabella presenta le seguenti caratteristiche principali:

  • ambiente: Database SQL di Azure (il livello è P4 Premium (500 DTU))
  • righe: 2.135.044.521
  • 1.275 partizioni utilizzate
  • indice cluster e partizionato

Modello

Questa è l'implementazione della tabella:

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

Il partizionamento è correlato a questo:

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à del servizio

Penso che gli indici e le statistiche siano ben mantenute ogni notte da ricostruzioni / riorganizzazioni / aggiornamenti incrementali.

Queste sono le statistiche dell'indice corrente delle partizioni di indice più utilizzate:

Statistiche delle partizioni

Queste sono le proprietà statistiche attuali delle partizioni più utilizzate:

statistica

Problema

Eseguo una semplice query su una frequenza elevata rispetto al tavolo.

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)

exce count

Il piano di esecuzione è simile al seguente: https://www.brentozar.com/pastetheplan/?id=rJvI_4TtG

Il mio problema è che queste query producono una quantità estremamente elevata di operazioni I / O con conseguente collo di bottiglia delle PAGEIOLATCH_SHattese.

le migliori attese

Domanda

Ho letto che le PAGEIOLATCH_SHattese sono spesso correlate a indici non ottimizzati. Ci sono dei consigli per me su come ridurre le operazioni di I / O? Forse aggiungendo un indice migliore?


Risposta 1 - correlata al commento di @ S4V1N

Il piano di query pubblicato proveniva da una query eseguita in SSMS. Dopo il tuo commento, faccio alcune ricerche sulla storia del server. La query accidentale eseguita dal servizio appare leggermente diversa (relativa a 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) 

Inoltre, il piano sembra diverso:

https://www.brentozar.com/pastetheplan/?id=H1fhALpKG

o

https://www.brentozar.com/pastetheplan/?id=S1DFQvpKz

E come puoi vedere qui, le nostre prestazioni del DB non sono influenzate da questa query.

Top SQL

Risposta 2: correlata alla risposta di @Joe Obbish

Per testare la soluzione ho sostituito Entity Framework con un semplice SqlCommand. Il risultato è stato un incredibile aumento delle prestazioni!

Il piano di query è ora lo stesso di SSMS e le letture e le scritture logiche scendono a ~ 8 per esecuzione.

Il carico I / O complessivo scende a quasi 0! Drop I / O

Spiega anche perché ottengo un forte calo delle prestazioni dopo aver modificato l'intervallo di partizioni da mensile a giornaliero. La mancanza dell'eliminazione delle partizioni ha comportato la scansione di più partizioni.


2
Guardando il piano di esecuzione, quella query non sembra affatto problematica: ha scansionato solo le partizioni necessarie con un basso numero di letture e non ha invece segnalato pageiolatch_sh attese (sos_sched ..). Il che è comprensibile perché non avevi comunque letture fisiche. Quelle attese cumulative o sono prese in un certo periodo di tempo? Forse il problema è dopo tutto un'altra domanda.
S4V1N,

Ho pubblicato una risposta dettagliata a te @ S4V1N sopra
Steffen Mangold

Risposte:


7

Potresti essere in grado di ridurre le PAGEIOLATCH_SHattese per questa query se sei in grado di modificare i tipi di dati generati dall'ORM. La Timestampcolonna nella tabella ha un tipo di dati DATETIMEma i parametri @p__linq__1e @p__linq__2i tipi di dati di DATETIME2(7). Questa differenza è il motivo per cui il piano di query per le query ORM è molto più complicato rispetto al primo piano di query pubblicato che aveva filtri di ricerca codificati. Puoi ottenere un suggerimento anche in XML:

<ScalarOperator ScalarString="GetRangeWithMismatchedTypes([@p__linq__1],NULL,(22))">

Così com'è, con la query ORM non è possibile ottenere l'eliminazione della partizione. Otterrai almeno alcune letture logiche per ogni partizione definita nella funzione di partizione, anche se stai solo cercando un giorno di dati. All'interno di ogni partizione si ottiene una ricerca dell'indice, quindi SQL Server non impiega molto a passare alla partizione successiva, ma forse tutto questo IO si sta sommando.

Ho fatto una semplice riproduzione per essere sicuro. Ci sono 11 partizioni definite all'interno della funzione di partizione. Per questa query:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime2(7) = '20180103';
DECLARE @p__linq__2 datetime2(7) = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

Ecco come si presenta IO:

Tabella 'DemoUnitData'. Conteggio scansioni 11, letture logiche 40

Quando aggiusto i tipi di dati:

DECLARE @p__linq__0 bigint = 2000;
DECLARE @p__linq__1 datetime = '20180103';
DECLARE @p__linq__2 datetime = '20180104';

SELECT 1 AS [C1]
, [Extent1].[Timestamp] AS [Timestamp]
, [Extent1].[Value1] AS [Value1]
FROM [DemoUnitData] AS [Extent1]  
WHERE ([Extent1].[UnitID] = @p__linq__0)  
AND ([Extent1].[Timestamp] >= @p__linq__1)  
AND ([Extent1].[Timestamp] < @p__linq__2)
OPTION (MAXDOP 1) ;

L'IO è ridotto a causa dell'eliminazione della partizione:

Tabella 'DemoUnitData'. Conteggio scansioni 2, letture logiche 8

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.