sql >> Databáze >  >> RDS >> Database

Chybějící indexy v MS SQL nebo optimalizace v žádném okamžiku

Při provádění dotazu se optimalizátor SQL Serveru snaží najít nejlepší plán dotazů na základě existujících indexů a dostupných nejnovějších statistik po rozumnou dobu, samozřejmě pokud tento plán již není uložen v mezipaměti serveru. Pokud ne, dotaz se provede podle tohoto plánu a plán se uloží do mezipaměti serveru. Pokud byl plán pro tento dotaz již vytvořen, dotaz se provede podle existujícího plánu.

Zajímá nás následující problém:

Pokud během sestavování plánu dotazů při řazení možných indexů server nenalezne nejlepší index, chybějící index se označí v plánu dotazů a server uchovává statistiky o takových indexech:kolikrát by server tento index použil a kolik by tento dotaz stál.

V tomto článku budeme tyto chybějící indexy analyzovat – jak s nimi naložit.

Zvažme to na konkrétním příkladu. Vytvořte několik tabulek v naší databázi na místním a testovacím serveru:

[rozbalit název =”Kód”]

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

[/expand]

Struktura je jednoduchá a skládá se ze dvou tabulek. První tabulka se nazývá objednávky s poli jako identifikátor, datum prodeje a prodejce. Druhým jsou detaily objednávky, kde je u některého zboží specifikována cena a množství.

Podívejte se na jednoduchý dotaz a jeho plán:

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

where d.cost > 1800

go

Na grafickém zobrazení plánu dotazů můžeme vidět zelenou nápovědu o chybějícím indexu. Pokud na něj kliknete pravým tlačítkem a vyberete „Chybí podrobnosti o indexu...“, zobrazí se text navrhovaného rejstříku. Jediné, co je třeba udělat, je odstranit komentáře v textu a pojmenovat index. Skript je připraven ke spuštění.

Nebudeme vytvářet index, který jsme obdrželi z nápovědy poskytované SSMS. Místo toho uvidíme, zda tento index doporučí dynamické pohledy napojené na chybějící indexy. Pohledy jsou následující:

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

Jak vidíme, v prvním pohledu jsou nějaké statistiky o chybějících indexech:

  1. Kolikrát by bylo provedeno vyhledávání, kdyby navrhovaný index existoval?
  2. Kolikrát by byla provedena kontrola, kdyby navrhovaný index existoval?
  3. Poslední datum a čas, kdy jsme index použili
  4. Aktuální skutečné náklady na plán dotazů bez navrhovaného indexu.

Druhý pohled je tělo indexu:

  1. Databáze
  2. Objekt/tabulka
  3. Seřazené sloupce
  4. Přidané sloupce za účelem zvýšení pokrytí indexem

Třetí pohled je kombinací prvního a druhého pohledu.

V souladu s tím není obtížné získat skript, který by generoval skript pro vytváření chybějících indexů z těchto dynamických zobrazení. Skript je následující:

[expand title=”Kód”]

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

[/expand]

Kvůli efektivitě indexu jsou na výstupu chybějící indexy. Perfektním řešením je, když tato sada výsledků nevrací nic. V našem příkladu sada výsledků vrátí alespoň jeden index:

Když není čas a nemáte chuť řešit klientské chyby, provedl jsem dotaz, zkopíroval první sloupec a provedl jej na serveru. Poté vše fungovalo dobře.

S informacemi na těchto indexech doporučuji zacházet vědomě. Například pokud systém doporučuje následující indexy:

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)

A tyto indexy se používají pro vyhledávání, je zcela zřejmé, že je logičtější nahradit tyto indexy takovým, který bude pokrývat všechny tři navrhované:

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

Proto provedeme kontrolu chybějících indexů před jejich nasazením na produkční server. Ačkoli…. Znovu jsem například nasadil ztracené indexy na server TFS, čímž jsem zvýšil celkový výkon. Provedení této optimalizace zabralo minimum času. Při přechodu z TFS 2015 na TFS 2017 jsem však čelil problému, že kvůli těmto novým indexům nedošlo k žádné aktualizaci. Přesto je lze snadno najít podle masky

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

Užitečný nástroj:

dbForge Index Manager – praktický doplněk SSMS pro analýzu stavu indexů SQL a řešení problémů s fragmentací indexů.


  1. Upsert s transakcí

  2. Mohli byste těžit z vlastní databázové aplikace?

  3. Jak vložit více řádků do jednoho SQL dotazu – otázka týdne #069

  4. Připojení zapalování k aplikaci Microsoft Access