Non so se si tratta di un bug, di per sé, ma è sicuramente un evento interessante. Le ricostruzioni delle partizioni online sono nuove in SQL Server 2014, quindi potrebbero esserci alcuni interni da ordinare con questo.
Ecco la mia migliore spiegazione per te. Le statistiche incrementali richiedono assolutamente che tutte le partizioni vengano campionate alla stessa velocità in modo che quando il motore unisce le pagine delle statistiche possa essere sicuro che la distribuzione campionata sia comparabile. REBUILD
campiona necessariamente i dati con una frequenza di campionamento del 100%. Non vi è alcuna garanzia che la frequenza di campionamento del 100% sulla partizione 9 sarà sempre la frequenza di campionamento esatta del resto delle partizioni. Per questo motivo, sembra che il motore non possa unire i campioni e si ottiene un blob di statistiche vuoto. Tuttavia, l'oggetto statistico è ancora lì:
select
check_time = sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'
È possibile riempire il BLOB con qualsiasi numero di mezzi:
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;
o
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);
oppure puoi attendere che AutoStats si aggiorni alla prima compilazione di un piano di query utilizzando quell'oggetto:
-- look at my creative query
select *
from dbo.TransactionHistory
where TransactionDate = '20140101';
Detto questo , questo post illuminante di Erin Stellato evidenzia ciò che è diventato percepito come una grave carenza di statistiche incrementali. I loro dati a livello di partizione non vengono utilizzati dall'ottimizzatore nella generazione del piano di query, riducendo il presunto vantaggio delle statistiche incrementali. Qual è, quindi, l'attuale vantaggio delle statistiche incrementali? Direi che la loro utilità principale è in grado di campionare tabelle di grandi dimensioni in modo più coerente a un ritmo più elevato rispetto alle statistiche tradizionali.
Usando il tuo esempio, ecco come appaiono le cose:
set statistics time on;
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 94 ms, elapsed time = 131 ms.
update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);
--SQL Server Execution Times:
-- CPU time = 0 ms, elapsed time = 5 ms.
drop index IDX_ProductId On dbo.TransactionHistory;
CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId)
WITH (DATA_COMPRESSION = ROW)
ON [PRIMARY]
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 76 ms, elapsed time = 66 ms.
Un aggiornamento completo delle statistiche sulla statistica incrementale costa 131 ms. Un aggiornamento delle statistiche fullscan sulla statistica non allineata alla partizione costa 66 ms. La statistica non allineata è molto più lenta molto probabilmente a causa del sovraccarico dovuto alla fusione delle singole pagine statistiche nell'istogramma principale. Tuttavia, utilizzando l'oggetto statistico allineato alle partizioni, è possibile aggiornare una partizione e fonderla nuovamente nel BLOB dell'istogramma principale in 5 ms. Quindi a questo punto l'amministratore con la statistica incrementale deve affrontare una decisione. Possono ridurre i tempi di manutenzione delle statistiche complessive aggiornando solo le partizioni che tradizionalmente dovrebbero essere aggiornate, oppure possono sperimentare frequenze di campionamento più elevate in modo tale da poter campionare più righe nello stesso periodo di tempo del periodo di manutenzione precedente. Il primo consente di respirare nella finestra di manutenzione, il secondo potrebbe spingere le statistiche su un tavolo molto grande in un luogo in cui le query ottengono piani migliori basati su statistiche più accurate. Questa non è una garanzia e il tuo chilometraggio può variare.
Il lettore può vedere che 66 ms non è un doloroso tempo di aggiornamento delle statistiche su questa tabella, quindi ho provato a impostare un test sul set di dati stackexchange. Ci sono 6.418.608 post (esclusi i post StackOverflow e tutti i post del 2012 - un errore di dati da parte mia) nel recente dump che ho scaricato.
Ho partizionato i dati [CreationDate]
perché ... demo.
Ecco alcuni tempi per alcuni scenari piuttosto standard (100% - ricostruzione indice, impostazione predefinita - aggiornamento automatico delle statistiche o UPDATE STATISTICS
senza una frequenza di campionamento specificata:
- Crea statistica non incrementale con FullScan: tempo CPU = 23500 ms, tempo trascorso = 22521 ms.
- Crea statistica incrementale con FullScan: tempo CPU = 20406 ms, tempo trascorso = 15413 ms.
- Aggiorna statistica non incrementale con frequenza di campionamento predefinita: tempo CPU = 406 ms, tempo trascorso = 408 ms.
- Aggiorna statistica incrementale con frequenza di campionamento predefinita: tempo CPU = 453 ms, tempo trascorso = 507 ms.
Diciamo che siamo più sofisticati di questi scenari predefiniti e abbiamo deciso che una frequenza di campionamento del 10% è la frequenza minima che dovrebbe portarci i piani di cui abbiamo bisogno mantenendo i tempi di manutenzione a un lasso di tempo ragionevole.
- Aggiorna statistica non incrementale con il 10 percento del campione: tempo CPU = 2344 ms, tempo trascorso = 2441 ms.
- Aggiorna statistica incrementale con il 10 percento di esempio: tempo CPU = 2344 ms, tempo trascorso = 2388 ms.
Finora non ci sono chiari vantaggi nell'avere una statistica incrementale. Tuttavia, se sfruttiamo il DMV non documentato sys.dm_db_stats_properties_internal()
(di seguito), puoi ottenere alcune informazioni su quali partizioni potresti voler aggiornare. Supponiamo di aver apportato modifiche ai dati nella partizione 3 e desideriamo assicurarci che le statistiche siano aggiornate per le query in arrivo. Ecco le nostre opzioni:
- Aggiornamento non incrementale al valore predefinito (anche il comportamento predefinito dell'aggiornamento automatico delle statistiche): 408 ms.
- Aggiornamento non incrementale al 10%: 2441 ms.
- Aggiorna statistiche incrementali, partizione 3 con Resample (10% - la nostra frequenza di campionamento definita): tempo CPU = 63 ms, tempo trascorso = 63 ms.
Ecco dove dobbiamo prendere una decisione. Prendiamo la vittoria di un 63 ms. aggiornamento delle statistiche basato su partizioni o aumentiamo ulteriormente la frequenza di campionamento? Diciamo che siamo disposti a prendere il colpo iniziale di campionamento al 50% su una statistica incrementale:
- Aggiorna statistiche incrementali al 50%: tempo trascorso = 16840 ms.
- Aggiorna statistiche incrementali, partizione 3 con Resample (50% - il nostro nuovo tempo di aggiornamento): tempo trascorso = 295 ms.
Siamo in grado di campionare molti più dati, forse impostando l'ottimizzatore per fare ipotesi migliori sui nostri dati (anche se non utilizza ancora statistiche a livello di partizione) e siamo in grado di farlo più rapidamente ora che abbiamo statistiche incrementali.
Un'ultima cosa divertente da capire, però. Che dire degli aggiornamenti delle statistiche sincrone? La frequenza di campionamento del 50% viene preservata anche quando si avvia l'autostrada?
Ho cancellato i dati dalla partizione 3 ed eseguito una query su CreationDate e verificato, quindi ho controllato le tariffe con la stessa query di seguito. La frequenza di campionamento del 50% è stata preservata.
Quindi, per farla breve: le statistiche incrementali possono essere uno strumento utile con la giusta quantità di pensiero e il lavoro di impostazione iniziale. Tuttavia, è necessario conoscere il problema che si sta tentando di risolvere e quindi è necessario risolverlo in modo appropriato. Se stai ottenendo stime errate sulla cardinalità, potresti essere in grado di ottenere piani migliori con una frequenza di campionamento strategica e alcuni interventi investiti. Tuttavia, stai ottenendo solo una piccola parte del vantaggio poiché l'istogramma utilizzato è la singola pagina delle statistiche unita e non le informazioni a livello di partizione. Se senti dolore nella finestra di manutenzione, forse le statistiche incrementali possono aiutarti, ma probabilmente ti richiederà di impostare un processo di intervento di manutenzione ad alto tocco. Senza riguardo,:
- Statistiche create con indici non allineati alle partizioni con la tabella di base.
- Statistiche create su database secondari leggibili AlwaysOn.
- Statistiche create su database di sola lettura.
- Statistiche create su indici filtrati.
- Statistiche create su viste.
- Statistiche create su tabelle interne.
- Statistiche create con indici spaziali o indici XML.
Spero che sia di aiuto
select
sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
parition_number = isnull(sp.partition_number,1),
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter = coalesce(sp.modification_counter, n1.modification_counter)
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
and (
(is_incremental = 0)
or
(is_incremental = 1 and sp.partition_number is not null)
)
and t.name = 'Posts'
and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)