2009-08-25 11 views
7

Mamy problemy z wydajnością bazy danych i mam duże doświadczenie z profilerami .NET i zawsze wykonuję analizę w aplikacji, ale podobnie jak wielu programistów czekam teraz aż do naprawdę spóźnionego (gdy jest to problem), aby rozpocząć analizę i próbować zebrać dane dotyczące sposobu rozwiązania problemu.SQL Profiler and Tuning Advisor

To prawdopodobnie nie będzie jeden post odpowiedzi, ale raczej post "POMÓŻ JESTEM DB IDIOT" i szukam w dowolnym kierunku osobistej porady, zaleceń i doświadczenia, jak wytropić problemy.

Jeśli chodzi o konfigurację, używamy SQL 2005, mam bardzo ograniczony dostęp do produkcji, aby uruchamiać program SQL Database Engine Tuning Advisor, a program SQL Profiler za pośrednictwem interfejsu portalu, mogę kopiować i wklejać, ale to wszystko. Jedną z kluczowych rzeczy, którą chciałbym zrobić, jest uzyskanie prawdziwego zdjęcia zapytań i wywołań produkcyjnych, dzięki czemu mogę załadować je do silnika tuningowego w niższym środowisku, w którym mogę spróbować przybić DB, aby uzyskać zalecenia od silnika. Doradca.

Odpowiedz

7

Skrypt ten może być wykorzystany do określenia, czy masz wybrał odpowiednie indeksy. Należy sprawdzić, jak często indeks jest używany do wyszukiwania i porównywać go z częstotliwością aktualizacji indeksu. Poszukiwanie wydajności odbywa się kosztem wydajności aktualizacji. Co gorsza, kiedy indeks jest często aktualizowany, indeks jest fragmentowany, a statystyki nieaktualne.

Powinieneś także porównać wartość range_scan_count z singleton_lookup_count. Skanowanie zakresu jest preferowane przed wyszukiwaniem singleton. Wyszukiwanie singleton może być przyczyną poszukiwania indeksu i operacji wyszukiwania klucza. Oznacza to, że dla każdego wiersza znalezionego w indeksie seek, sql będzie szukał datapage w indeksie klastrowym, a to jest w porządku, powiedzmy kilka tysięcy, ale nie dla milionów wierszy.

CREATE PROCEDURE [ADMIN].[spIndexCostBenefit] 
    @dbname [nvarchar](75) 
WITH EXECUTE AS CALLER 
AS 
--set @dbname='Chess' 
declare @dbid nvarchar(5) 
declare @sql nvarchar(2000) 
select @dbid = convert(nvarchar(5),db_id(@dbname)) 

set @sql=N'select ''object'' = object_name(iu.object_id, iu.database_id) 
     , i.name 
     ,''user reads'' = iu.user_seeks + iu.user_scans + iu.user_lookups 
     ,''system reads'' = iu.system_seeks + iu.system_scans + iu.system_lookups 
     ,''user writes'' = iu.user_updates 
     ,''system writes'' = iu.system_updates 
from '+ @dbname + '.sys.dm_db_index_usage_stats iu 
,' + @dbname + '.sys.indexes i 
where 
    iu.database_id = ' + @dbid + ' 
    and iu.index_id=i.index_id 
    and iu.object_id=i.object_id 
    and (iu.user_seeks + iu.user_scans + iu.user_lookups)<iu.user_updates 
order by ''user reads'' desc' 

exec sp_executesql @sql 

set @sql=N'SELECT 
    ''object'' = object_name(o.object_id, o.database_id), 
    o.index_id, 
    ''usage_reads'' = user_seeks + user_scans + user_lookups, 
    ''operational_reads'' = range_scan_count + singleton_lookup_count, 
    range_scan_count, 
    singleton_lookup_count, 
    ''usage writes'' = user_updates, 
    ''operational_leaf_writes'' = leaf_insert_count + leaf_update_count + leaf_delete_count, 
    leaf_insert_count, 
    leaf_update_count, 
    leaf_delete_count, 
    ''operational_leaf_page_splits'' = leaf_allocation_count, 
    ''operational_nonleaf_writes'' = nonleaf_insert_count + nonleaf_update_count + nonleaf_delete_count, 
    ''operational_nonleaf_page_splits'' = nonleaf_allocation_count 
FROM 
    ' + @dbname + '.sys.dm_db_index_operational_stats(' + @dbid + ', NULL, NULL, NULL) o, 
    ' + @dbname + '.sys.dm_db_index_usage_stats u 
WHERE 
    u.object_id = o.object_id 
    AND u.index_id = o.index_id 
ORDER BY 
    operational_reads DESC, 
    operational_leaf_writes, 
    operational_nonleaf_writes' 

exec sp_executesql @sql 

GO 
5

Jeśli można użyć profilera do przechowywania zdarzeń do stołu, to nie ma problemu, aby skorzystać z doradcą Tuning Database (DTA), aby zoptymalizować bazę danych z tabeli dziennika, ale osobiście w ogóle nie korzystam z DTA. Zużycie DTA wymaga dużo czasu i chcę mieć większą kontrolę nad tym, co się dzieje.

Jeśli możesz przekonać właściciela serwera do utworzenia nowej bazy danych o nazwie "SQLToolkit" i przyznać ci uprawnienia do wykonywania procedur, to mam kilka procedur, które pomogą Ci wybrać właściwe indeksy.

CREATE PROCEDURE [ADMIN].[spMissingIndexes] 
AS 
SELECT 
     mid.statement, 
     mid.equality_columns, 
     mid.inequality_columns, 
     mid.included_columns, 
     migs.user_seeks, 
     migs.user_scans, 
     migs.last_user_seek, 
     migs.avg_user_impact, 
     user_scans, 
     avg_total_user_cost, 
     avg_total_user_cost * avg_user_impact * (user_seeks + user_scans) AS [weight]--, migs.*--, mid.* 
    FROM 
     sys.dm_db_missing_index_group_stats AS migs 
     INNER JOIN sys.dm_db_missing_index_groups AS mig 
     ON (migs.group_handle = mig.index_group_handle) 
     INNER JOIN sys.dm_db_missing_index_details AS mid 
     ON (mig.index_handle = mid.index_handle) 
    ORDER BY 
     avg_total_user_cost * avg_user_impact * (user_seeks + user_scans) DESC ; 

GO 
+0

To było wspaniałe dzięki! Jeśli masz jakieś dodatkowe, które nie masz nic przeciwko dzieleniu, chciałbym je zobaczyć. Jeszcze raz dziękuję! – Nic

4

Na życzenie mogę wysłać kolejny przydatny skrypt, aby określić, jak często i jak długo każdy indeks jest zablokowane ze względu na mechanizm blokujący w SQL:

CREATE PROCEDURE [ADMIN].[spIndexContention] 
    @dbname sysname 
WITH EXECUTE AS CALLER 
AS 
declare @dbid int 
select @dbid = DB_ID(@dbname) 
declare @sql nvarchar(1000) 
SET @sql = N'SELECT dbname=DB_NAME(database_id), tablename=object_name(s.object_id, s.database_id) 
    , indexname=i.name, i.index_id 
    , row_lock_count, row_lock_wait_count 
    , [block %]=cast (100.0 * row_lock_wait_count/(1 + row_lock_count) as numeric(15,2)) 
    , row_lock_wait_in_ms 
    , [avg row lock waits in ms]=cast (1.0 * row_lock_wait_in_ms/(1 + row_lock_wait_count) as numeric(15,2)) 
FROM sys.dm_db_index_operational_stats (' + convert(nvarchar(5),@dbid) + ', NULL, NULL, NULL) s 
    INNER JOIN ' + @dbname + N'.sys.indexes i 
     ON i.object_id = s.object_id 
     AND i.index_id = s.index_id 
ORDER BY row_lock_wait_count desc' 
print @sql 
exec sp_executesql @sql 


GO