Database
 sql >> Baza danych >  >> RDS >> Database

Brakujące indeksy w MS SQL lub błyskawiczna optymalizacja

Podczas wykonywania zapytania optymalizator programu SQL Server próbuje znaleźć najlepszy plan zapytania na podstawie istniejących indeksów i dostępnych najnowszych statystyk przez rozsądny czas, oczywiście, jeśli plan ten nie jest już przechowywany w pamięci podręcznej serwera. Jeśli nie, zapytanie jest wykonywane zgodnie z tym planem, a plan jest przechowywany w pamięci podręcznej serwera. Jeśli plan został już zbudowany dla tego zapytania, zapytanie jest wykonywane zgodnie z istniejącym planem.

Interesuje nas następujący problem:

Podczas kompilacji planu zapytania, przy sortowaniu możliwych indeksów, jeśli serwer nie znajdzie najlepszego indeksu, brakujący indeks jest zaznaczany w planie zapytania, a serwer prowadzi statystyki dotyczące takich indeksów:ile razy serwer wykorzystałby ten indeks i ile kosztowałoby to zapytanie.

W tym artykule przeanalizujemy te brakujące indeksy – jak sobie z nimi radzić.

Rozważmy to na konkretnym przykładzie. Utwórz kilka tabel w naszej bazie danych na serwerze lokalnym i testowym:

[rozwiń tytuł =”Kod”]

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

[/rozwiń]

Struktura jest prosta i składa się z dwóch tabel. Pierwsza tabela to zamówienia z takimi polami jak identyfikator, data sprzedaży i sprzedawca. Drugi to szczegóły zamówienia, gdzie niektóre towary są wyszczególnione wraz z ceną i ilością.

Spójrz na proste zapytanie i jego plan:

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

where d.cost > 1800

go

Na graficznym wyświetlaczu planu zapytania widzimy zieloną podpowiedź o brakującym indeksie. Jeśli klikniesz go prawym przyciskiem myszy i wybierzesz „Brakujące szczegóły indeksu ..”, pojawi się tekst sugerowanego indeksu. Jedyne, co należy zrobić, to usunąć komentarze z tekstu i nadać indeksowi nazwę. Skrypt jest gotowy do wykonania.

Nie będziemy budować indeksu, który otrzymaliśmy z podpowiedzi dostarczonej przez SSMS. Zamiast tego zobaczymy, czy ten indeks będzie rekomendowany przez dynamiczne widoki połączone z brakującymi indeksami. Widoki są następujące:

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 widać, w pierwszym widoku dostępne są statystyki dotyczące brakujących indeksów:

  1. Ile razy zostałoby wykonane wyszukiwanie, gdyby istniał sugerowany indeks?
  2. Ile razy zostałoby wykonane skanowanie, gdyby istniał sugerowany indeks?
  3. Ostatnia data i godzina użycia indeksu
  4. Aktualny rzeczywisty koszt planu zapytania bez sugerowanego indeksu.

Drugi widok to treść indeksu:

  1. Baza danych
  2. Obiekt/tabela
  3. Posortowane kolumny
  4. Dodano kolumny w celu zwiększenia zasięgu indeksu

Trzeci widok to połączenie pierwszego i drugiego widoku.

W związku z tym nie jest trudno uzyskać skrypt, który wygeneruje skrypt do tworzenia brakujących indeksów z tych dynamicznych widoków. Skrypt wygląda następująco:

[rozwiń tytuł=”Kod”]

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

[/rozwiń]

Dla wydajności indeksu wyprowadzane są brakujące indeksy. Idealnym rozwiązaniem jest sytuacja, gdy ten zestaw wyników nic nie zwraca. W naszym przykładzie zestaw wyników zwróci co najmniej jeden indeks:

Kiedy nie ma czasu i nie masz ochoty zajmować się błędami klienta, wykonałem zapytanie, skopiowałem pierwszą kolumnę i wykonałem na serwerze. Potem wszystko działało dobrze.

Polecam świadomie traktować informacje o tych indeksach. Na przykład, jeśli system zaleca następujące indeksy:

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 te indeksy są używane do wyszukiwania, jest całkiem oczywiste, że bardziej logiczne jest zastąpienie tych indeksów jednym, który obejmie wszystkie trzy sugerowane:

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

Dlatego dokonujemy przeglądu brakujących indeksów przed wdrożeniem ich na serwer produkcyjny. Mimo że…. Ponownie, na przykład, wdrożyłem utracone indeksy na serwerze TFS, zwiększając w ten sposób ogólną wydajność. Wykonanie tej optymalizacji zajęło minimum czasu. Jednak przy zmianie z TFS 2015 na TFS 2017 napotkałem problem, że nie było aktualizacji z powodu tych nowych indeksów. Niemniej jednak można je łatwo znaleźć po masce

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

Przydatne narzędzie:

dbForge Index Manager – poręczny dodatek SSMS do analizy stanu indeksów SQL i rozwiązywania problemów z fragmentacją indeksów.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Rozwiązywanie problemów z wydajnością procesora w VMware

  2. Jak śledzić to, co robią użytkownicy?

  3. Operator SQL BETWEEN dla początkujących

  4. Kilka sposobów na wstawienie dzielonych ciągów rozdzielanych w kolumnie

  5. Modele danych