Database
 sql >> Database >  >> RDS >> Database

Indici mancanti in MS SQL o ottimizzazione in pochissimo tempo

Durante l'esecuzione di una query, l'ottimizzatore di SQL Server tenta di trovare il miglior piano di query in base agli indici esistenti e alle statistiche più recenti disponibili per un periodo di tempo ragionevole, ovviamente, se questo piano non è già archiviato nella cache del server. In caso negativo, la query viene eseguita in base a questo piano e il piano viene archiviato nella cache del server. Se il piano è già stato creato per questa query, la query viene eseguita in base al piano esistente.

Siamo interessati al seguente problema:

Durante la compilazione di un piano di query, durante l'ordinamento di possibili indici, se il server non trova l'indice migliore, l'indice mancante viene contrassegnato nel piano di query e il server conserva le statistiche su tali indici:quante volte il server utilizzerebbe questo indice e quanto costerebbe questa query.

In questo articolo, analizzeremo questi indici mancanti:come gestirli.

Consideriamo questo su un esempio particolare. Crea un paio di tabelle nel nostro database su un server locale e di prova:

[espandi titolo =”Codice”]

if object_id ('orders_detail') is not null drop table orders_detail;

if object_id('orders') is not null drop table orders;

go

create table orders

(

id int identity primary key,

dt datetime,

seller nvarchar(50)

)

create table orders_detail

(

id int identity primary key,

order_id int foreign key references orders(id),

product nvarchar(30),

qty int,

price money,

cost as qty * price

)

go

with cte as

(

select 1 id union all

select id+1 from cte where id < 20000

)

insert orders

select

dt,

seller

from

(

select

dateadd(day,abs(convert(int,convert(binary(4),newid()))%365),'2016-01-01') dt,

abs(convert(int,convert(binary(4),newid()))%5)+1 seller_id

from cte

) c

left join

(

values
(1,'John'),

(2,'Mike'),

(3,'Ann'),

(4,'Alice'),

(5,'George')
) t (id,seller) on t.id = c.seller_id

option(maxrecursion 0)

 

insert orders_detail

select

order_id,

product,

qty,

price

from

(

select

o.id as order_id,

abs(convert(int,convert(binary(4),newid()))%5)+1 product_id,

abs(convert(int,convert(binary(4),newid()))%20)+1 qty

from orders o cross join

(

select top(abs(convert(int,convert(binary(4),newid()))%5)+1) *

from

(

values (1),(2),(3),(4),(5),(6),(7),(8)

) n(num)

) n

) c

left join

(

values
(1,'Sugar', 50),

(2,'Milk', 80),

(3,'Bread', 20),

(4,'Pasta', 40),

(5,'Beer', 100)

) t (id,product, price) on t.id = c.product_id

go

[/espandi]

La struttura è semplice ed è composta da due tavoli. La prima tabella è chiamata ordini con campi come identificatore, data di vendita e venditore. Il secondo sono i dettagli dell'ordine, in cui alcune merci sono specificate con prezzo e quantità.

Guarda una semplice query e il suo piano:

select count(*) from orders o join orders_detail d on o.id = d.order_id

where d.cost > 1800

go

Possiamo vedere un suggerimento verde sull'indice mancante sul display grafico del piano di query. Se fai clic con il pulsante destro del mouse e selezioni "Dettagli indice mancanti ..", ci sarà il testo dell'indice suggerito. L'unica cosa da fare è rimuovere i commenti nel testo e dare un nome all'indice. Lo script è pronto per essere eseguito.

Non costruiremo l'indice che abbiamo ricevuto dal suggerimento fornito da SSMS. Vedremo invece se questo indice sarà consigliato da viste dinamiche collegate a indici mancanti. Le visualizzazioni sono le seguenti:

select * from sys.dm_db_missing_index_group_stats

select * from sys.dm_db_missing_index_details

select * from sys.dm_db_missing_index_groups

Come possiamo vedere, ci sono alcune statistiche sugli indici mancanti nella prima vista:

  1. Quante volte verrebbe eseguita una ricerca se esistesse l'indice suggerito?
  2. Quante volte verrebbe eseguita una scansione se esistesse l'indice suggerito?
  3. Ultima data e ora in cui abbiamo utilizzato l'indice
  4. Il costo reale attuale del piano di query senza l'indice suggerito.

La seconda vista è il corpo dell'indice:

  1. Banca dati
  2. Oggetto/tabella
  3. Colonne ordinate
  4. Colonne aggiunte per aumentare la copertura dell'indice

La terza vista è la combinazione della prima e della seconda vista.

Di conseguenza, non è difficile ottenere uno script che generi uno script per creare indici mancanti da queste viste dinamiche. Lo script è il seguente:

[expand title=”Codice”]

with igs as

(

select *

from sys.dm_db_missing_index_group_stats

)

, igd as

(

select *,

isnull(equality_columns,'')+','+isnull(inequality_columns,'') as ix_col

from sys.dm_db_missing_index_details

)

select --top(10)

'use ['+db_name(igd.database_id)+'];

create index ['+'ix_'+replace(convert(varchar(10),getdate(),120),'-','')+'_'+convert(varchar,igs.group_handle)+'] on '+

igd.[statement]+'('+

case

when left(ix_col,1)=',' then stuff(ix_col,1,1,'')

when right(ix_col,1)=',' then reverse(stuff(reverse(ix_col),1,1,''))

else ix_col

end

+') '+isnull('include('+igd.included_columns+')','')+' with(online=on, maxdop=0)

go

' command

,igs.user_seeks

,igs.user_scans

,igs.avg_total_user_cost

from igs

join sys.dm_db_missing_index_groups link on link.index_group_handle = igs.group_handle

join igd on link.index_handle = igd.index_handle

where igd.database_id = db_id()

order by igs.avg_total_user_cost * igs.user_seeks desc

[/espandi]

Per l'efficienza dell'indice, vengono emessi gli indici mancanti. La soluzione perfetta è quando questo set di risultati non restituisce nulla. Nel nostro esempio, il set di risultati restituirà almeno un indice:

Quando non c'è tempo e non hai voglia di affrontare i bug del client, ho eseguito la query, copiato la prima colonna e l'ho eseguita sul server. Dopo questo, tutto ha funzionato bene.

Raccomando di trattare consapevolmente le informazioni su questi indici. Ad esempio, se il sistema consiglia i seguenti indici:

create index ix_01 on tbl1 (a,b) include (c)

create index ix_02 on tbl1 (a,b) include (d)

create index ix_03 on tbl1 (a)

E questi indici sono usati per la ricerca, è abbastanza ovvio che è più logico sostituire questi indici con uno che copra tutti e tre suggeriti:

create index ix_1 on tbl1 (a,b) include (c,d)

Pertanto, esaminiamo gli indici mancanti prima di distribuirli al server di produzione. Sebbene…. Ancora una volta, ad esempio, ho distribuito gli indici persi sul server TFS, aumentando così le prestazioni complessive. Ci è voluto un tempo minimo per eseguire questa ottimizzazione. Tuttavia, quando sono passato da TFS 2015 a TFS 2017, ho riscontrato il problema che non c'erano aggiornamenti a causa di questi nuovi indici. Tuttavia, possono essere facilmente trovati dalla maschera

select * from sys.indexes where name like 'ix[_]2017%'

Strumento utile:

dbForge Index Manager – pratico componente aggiuntivo SSMS per analizzare lo stato degli indici SQL e risolvere i problemi con la frammentazione degli indici.