Quando hai una query o una procedura memorizzata che richiede l'ottimizzazione delle prestazioni, quali sono alcune delle prime cose che provi?
Quando hai una query o una procedura memorizzata che richiede l'ottimizzazione delle prestazioni, quali sono alcune delle prime cose che provi?
Risposte:
Ecco la comoda lista di cose che do sempre a qualcuno che mi chiede l'ottimizzazione.
Utilizziamo principalmente Sybase, ma la maggior parte dei consigli si applicherà su tutta la linea.
SQL Server, ad esempio, viene fornito con una serie di bit di monitoraggio / ottimizzazione delle prestazioni, ma se non hai nulla del genere (e forse anche se lo fai), prenderei in considerazione quanto segue ...
Il 99% dei problemi che ho riscontrato sono causati dall'inserimento di un numero eccessivo di tabelle in un join . La soluzione per questo è fare metà del join (con alcune delle tabelle) e memorizzare nella cache i risultati in una tabella temporanea. Quindi esegui il resto della query unendo quella tabella temporanea.
#temp
le tabelle possono funzionare molto meglio delle @table
variabili con grandi volumi (migliaia di righe).Leggermente fuori tema, ma se si ha il controllo su questi problemi ...
Alto livello e alto impatto.
CREATE INDEX
Assicurati che ci siano indici disponibili per la tua WHERE
e JOIN
clausole. Ciò accelererà notevolmente l'accesso ai dati.
Se il tuo ambiente è un data mart o un magazzino, gli indici dovrebbero abbondare per quasi tutte le query possibili.
In un ambiente transazionale , il numero di indici dovrebbe essere inferiore e le loro definizioni più strategiche in modo che la manutenzione dell'indice non trascini le risorse. (La manutenzione dell'indice è quando le foglie di un indice devono essere modificate per riflettere un cambiamento nella tabella sottostante, come con INSERT, UPDATE,
e DELETE
operazioni.)
Inoltre, fai attenzione all'ordine dei campi nell'indice: più un campo è selettivo (cardinalità più elevata), prima dovrebbe apparire nell'indice. Ad esempio, supponiamo che stai interrogando per le automobili usate:
SELECT i.make, i.model, i.price
FROM dbo.inventory i
WHERE i.color = 'red'
AND i.price BETWEEN 15000 AND 18000
Il prezzo ha generalmente una cardinalità più elevata. Potrebbero esserci solo poche decine di colori disponibili, ma probabilmente migliaia di prezzi di richiesta diversi.
Di queste scelte di indice, idx01
fornisce il percorso più veloce per soddisfare la query:
CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)
Questo perché un numero inferiore di auto soddisferà il prezzo rispetto alla scelta del colore, offrendo al motore di query molti meno dati da analizzare.
Sono stato conosciuto per avere due indici molto simili che differiscono solo nell'ordine dei campi per velocizzare le query (nome, cognome) in uno e (cognome, nome) nell'altro.
Un trucco che ho appreso di recente è che SQL Server può aggiornare variabili locali e campi, in una dichiarazione di aggiornamento.
UPDATE table
SET @variable = column = @variable + otherColumn
O la versione più leggibile:
UPDATE table
SET
@variable = @variable + otherColumn,
column = @variable
Ho usato questo per sostituire cursori / join complicati durante l'implementazione di calcoli ricorsivi e ho anche guadagnato molto in termini di prestazioni.
Ecco i dettagli e il codice di esempio che hanno apportato fantastici miglioramenti nelle prestazioni: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx
Supponendo che MySQL qui, utilizzare EXPLAIN per scoprire cosa sta succedendo con la query, assicurarsi che gli indici vengano utilizzati nel modo più efficiente possibile e provare a eliminare gli ordinamenti dei file. MySQL ad alte prestazioni: ottimizzazione, backup, replica e altro è un ottimo libro su questo argomento così come il blog sulle prestazioni di MySQL .
@Terrapin ci sono alcune altre differenze tra isnull e coalesce che vale la pena menzionare (oltre alla conformità ANSI, che è una grande per me).
In genere inizierò con i join: eliminerò ognuno di essi dalla query uno alla volta e rieseguirò la query per avere un'idea se esiste un join specifico con cui ho riscontrato un problema.
Su tutte le mie tabelle temporanee, mi piace aggiungere vincoli univoci (se del caso) per creare indici e chiavi primarie (quasi sempre).
declare @temp table(
RowID int not null identity(1,1) primary key,
SomeUniqueColumn varchar(25) not null,
SomeNotUniqueColumn varchar(50) null,
unique(SomeUniqueColumn)
)
Ho preso l'abitudine di usare sempre le variabili bind. È possibile che le variabili di bind non siano utili se RDBMS non memorizza nella cache le istruzioni SQL. Ma se non si utilizzano le variabili bind, RDBMS non ha la possibilità di riutilizzare i piani di esecuzione delle query e le istruzioni SQL analizzate. I risparmi possono essere enormi: http://www.akadia.com/services/ora_bind_variables.html . Lavoro principalmente con Oracle, ma Microsoft SQL Server funziona più o meno allo stesso modo.
Nella mia esperienza, se non sai se stai usando o meno le variabili bind, probabilmente non lo sei. Se la lingua della tua applicazione non li supporta, trova quella che lo supporta. A volte è possibile correggere la query A utilizzando le variabili di bind per la query B.
Dopodiché, parlo con il nostro DBA per scoprire cosa sta causando più dolore all'RDBMS. Nota che non dovresti chiedere "Perché questa query è lenta?" È come chiedere al tuo medico di portarti fuori l'appendice. Sicuramente la tua domanda potrebbe essere il problema, ma è altrettanto probabile che qualcos'altro vada storto. Come sviluppatori, tendiamo a pensare in termini di righe di codice. Se una linea è lenta, correggi quella linea. Ma un RDBMS è un sistema davvero complicato e la tua query lenta potrebbe essere il sintomo di un problema molto più grande.
Troppi suggerimenti di ottimizzazione SQL sono idoli di culto del carico. Il più delle volte il problema non è correlato o minimamente correlato alla sintassi che si utilizza, quindi di solito è meglio usare la sintassi più pulita possibile. Quindi puoi iniziare a cercare modi per ottimizzare il database (non la query). Modifica la sintassi solo quando fallisce.
Come qualsiasi tuning delle prestazioni, raccogli sempre statistiche significative. Non utilizzare l'ora del wallclock a meno che non sia l'esperienza utente che stai sintonizzando. Invece guarda cose come il tempo della CPU, le file recuperate e i blocchi letti dal disco. Troppo spesso le persone ottimizzano per la cosa sbagliata.
Eseguire la query utilizzando WITH (NoLock) è un'operazione piuttosto standard al posto mio. Chiunque è stato sorpreso a eseguire query sui tavoli da decine di gigabyte senza che sia rimosso e girato.
Converti le query NOT IN in JOIN ESTERNI SINISTRO, se possibile. Ad esempio, se si desidera trovare tutte le righe in Table1 che non sono utilizzate da una chiave esterna in Table2, è possibile farlo:
SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
SELECT Table1ID
FROM Table2)
Ma ottieni prestazioni molto migliori con questo:
SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
@ DavidM
Supponendo che MySQL qui, utilizzare EXPLAIN per scoprire cosa sta succedendo con la query, assicurarsi che gli indici vengano utilizzati nel modo più efficiente possibile ...
In SQL Server, il piano di esecuzione ti dà la stessa cosa: ti dice quali indici vengono colpiti, ecc.
Non necessariamente un trucco prestazionale di SQL in sé ma sicuramente correlato:
Una buona idea sarebbe quella di utilizzare memcached, ove possibile, poiché sarebbe molto più veloce semplicemente recuperare i dati precompilati direttamente dalla memoria piuttosto che ottenerli dal database. C'è anche un sapore di MySQL che ha memcached integrato (di terze parti).
Assicurati che le lunghezze dell'indice siano le più piccole possibili. Ciò consente al DB di leggere più chiavi alla volta dal file system, accelerando così i join. Presumo che funzioni con tutti i DB, ma so che è una raccomandazione specifica per MySQL.
Cerco:
SET NOCOUNT ON
Di solito la prima riga all'interno delle mie procedure memorizzate, a meno che non sia effettivamente necessario utilizzare @@ROWCOUNT
.
In SQL Server, utilizzare la direttiva nolock. Permette di completare il comando select senza dover aspettare - di solito terminano altre transazioni.
SELECT * FROM Orders (nolock) where UserName = 'momma'
Rimuovi le chiamate di funzione in Sprocs dove molte righe chiameranno la funzione.
Il mio collega ha usato le chiamate di funzione (ottenendo lastlogindate da userid come esempio) per restituire recordset molto ampi.
Compito con l'ottimizzazione, ho sostituito le chiamate di funzione nello sproc con il codice della funzione: il tempo di esecuzione di molti sprocs è passato da> 20 secondi a <1.
Mi piace usare
isnull(SomeColThatMayBeNull, '')
Al di sopra di
coalesce(SomeColThatMayBeNull, '')
Quando non ho bisogno del supporto per argomenti multipli che ti dà la coesione.
http://blog.falafel.com/2006/04/05/SQLServerArcanaISNULLVsCOALESCE.aspx
Non aggiungere il prefisso ai nomi delle Stored procedure con "sp_" perché tutte le procedure di sistema iniziano con "sp_" e SQL Server dovrà effettuare ricerche più difficili per trovare la procedura quando viene chiamata.
set transaction isolation level read uncommitted
Previene i dead dead in cui l'integrità transazionale non è assolutamente necessaria (che di solito è vera)
Vado sempre a SQL Profiler (se si tratta di una procedura memorizzata con molti livelli di annidamento) o al pianificatore di esecuzione della query (se si tratta di alcune istruzioni SQL senza annidamento) per primo. Il 90% delle volte è possibile trovare immediatamente il problema con uno di questi due strumenti.