2009-08-25 16 views
7

Abbiamo problemi con le prestazioni del database, e ho molta esperienza con .NET Profilers e eseguo sempre le analisi sull'app, ma come molti sviluppatori che sto aspettando fino a tardi (quando è un problema) per iniziare ad analizzare e provare a raccogliere i dati su come risolvere il problema.SQL Profiler and Tuning Advisor

Questo probabilmente non sarà un post di una sola risposta solo più di un post "HELP I AM A DB IDIOT" e alla ricerca di qualsiasi consiglio personale di direzione, consigli ed esperienza su come rintracciare i problemi.

Per quanto riguarda l'installazione, utilizziamo SQL 2005, ho un accesso molto limitato in produzione per eseguire solo Ottimizzazione guidata motore di database SQL e SQL Profiler tramite un'interfaccia portale, posso copiare e incollare ma è tutto. Una cosa fondamentale che vorrei fare è ottenere una vera istantanea delle query di produzione e delle chiamate, in modo che io possa caricarle nel motore di sintonizzazione in un ambiente più basso che posso provare a inchiodare il DB in modo da ottenere i suggerimenti dall'ottimizzazione del motore Consulente.

risposta

7

Questo script può essere utilizzato per determinare se si dispone scelto gli indici giusti È necessario considerare la frequenza con cui l'indice viene utilizzato per la ricerca e confrontarlo con la frequenza con cui l'indice viene aggiornato. Cerca prestazioni a costo di prestazioni di aggiornamento. E quel che è peggio, quando l'indice viene aggiornato di frequente, si fa in modo che l'indice sia frammentato e le statistiche non siano aggiornate.

È inoltre necessario confrontare il range_scan_count a singleton_lookup_count. La scansione di intervallo è preferibile prima della ricerca di singleton. Una ricerca singleton può essere la causa e l'indice di ricerca e un'operazione di ricerca della chiave. Cioè, per ogni riga trovata nell'indice seek, sql cercherà il datapage nell'indice cluster, e va bene se diciamo un paio di migliaia, ma non per milioni di righe.

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

Se è possibile utilizzare il profiler per memorizzare gli eventi da una tabella, quindi non è un problema di usare il consigliere di sintonia Database (DTA) per ottimizzare il database dalla tabella dei registri, ma personalmente non uso affatto la DTA. Ci vuole un sacco di tempo per usare la DTA e voglio un maggiore controllo su ciò che sta accadendo.

Se riesci a convincere il proprietario del server a creare un nuovo database chiamato qualcosa di "SQLToolkit" e darti diritti di esecuzione sulle procedure, allora ho un paio di procedure che ti aiuteranno a scegliere gli indici corretti.

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

Questo è stato fantastico grazie! Se ne hai di aggiuntivi che non ti dispiace condividere mi piacerebbe vederli. Grazie ancora! – Nic

4

Su richiesta i inviare un altro script utile per determinare la frequenza e per quanto tempo qualsiasi indice è bloccato a causa del meccanismo di blocco in 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