Trucchi di ottimizzazione delle prestazioni preferiti [chiuso]


126

Quando hai una query o una procedura memorizzata che richiede l'ottimizzazione delle prestazioni, quali sono alcune delle prime cose che provi?



Sono d'accordo che questo non è costruttivo e può essere cercato su Google, ma perché ha 118 uv ?! :)
FLICKER,

Risposte:


114

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.

Elenco di controllo per l'ottimizzazione delle query

  • Esegui UPDATE STATISTICS sulle tabelle sottostanti
    • Molti sistemi eseguono questo come un lavoro settimanale programmato
  • Elimina i record dalle tabelle sottostanti (eventualmente archivia i record eliminati)
    • Valuta di farlo automaticamente una volta al giorno o una volta alla settimana.
  • Ricostruisci gli indici
  • Ricostruisci tabelle (uscita / ingresso dati bcp)
  • Scarica / Ricarica il database (drastico, ma potrebbe correggere la corruzione)
  • Crea un nuovo indice più appropriato
  • Eseguire DBCC per vedere se è possibile corruzione nel database
  • Blocchi / deadlock
    • Assicurarsi che nessun altro processo sia in esecuzione nel database
      • Soprattutto DBCC
    • Stai utilizzando il blocco a livello di riga o di pagina?
    • Blocca le tabelle esclusivamente prima di iniziare la query
    • Verificare che tutti i processi accedano alle tabelle nello stesso ordine
  • Gli indici vengono utilizzati in modo appropriato?
    • I join utilizzeranno l'indice solo se entrambe le espressioni sono esattamente dello stesso tipo di dati
    • L'indice verrà utilizzato solo se i primi campi nell'indice corrispondono nella query
    • Gli indici cluster sono usati dove appropriato?
      • intervallo di dati
      • DOVE campo tra valore1 e valore2
  • I Small Joins sono Nice Joins
    • Per impostazione predefinita, l'ottimizzatore prenderà in considerazione solo le tabelle 4 alla volta.
    • Ciò significa che in join con più di 4 tabelle, ha buone probabilità di scegliere un piano di query non ottimale
  • Rompere il Join
    • Puoi rompere il join?
    • Preseleziona le chiavi esterne in una tabella temporanea
    • Fai metà del join e inserisci i risultati in una tabella temporanea
  • Stai usando il giusto tipo di tabella temporanea?
    • #temple tabelle possono funzionare molto meglio delle @tablevariabili con grandi volumi (migliaia di righe).
  • Mantieni tabelle riassuntive
    • Costruisci con i trigger nelle tabelle sottostanti
    • Costruisci ogni giorno / ogni ora / ecc.
    • Crea ad-hoc
    • Costruire in modo incrementale o demolizione / ricostruzione
  • Scopri qual è il piano di query con SET SHOWPLAN ON
  • Guarda cosa sta realmente accadendo con SET STATS IO ON
  • Forzare un indice usando il pragma: (indice: myindex)
  • Forzare l'ordine delle tabelle usando SET FORCEPLAN ON
  • Parameter Sniffing:
    • Rompere la procedura memorizzata in 2
    • chiama proc2 da proc1
    • consente all'ottimizzatore di scegliere l'indice in proc2 se @parameter è stato modificato da proc1
  • Puoi migliorare il tuo hardware?
  • A che ora corri? C'è un momento più tranquillo?
  • Replication Server (o altro processo non-stop) è in esecuzione? Puoi sospenderlo? Eseguilo ad es. oraria?

2
a quale bit ti riferisci?
AJ.

2
Queste sono cose interessanti, ma vorrei che avessi dei riferimenti per alcune affermazioni. Ad esempio: non avevo mai sentito che l'ottimizzazione considera solo 4 tabelle alla volta in un join. Non capisco come possa essere giusto. Potresti fornire qualche riferimento in particolare? Mi piacerebbe vedere dove lo stai ottenendo.
SheldonH,

19
  1. Avere una buona idea del percorso ottimale di esecuzione della query nella tua testa.
  2. Controlla il piano di query - sempre.
  3. Attiva STATS, in modo da poter esaminare le prestazioni di IO e CPU. Concentrati sulla riduzione di tali numeri, non necessariamente sul tempo di interrogazione (poiché ciò può essere influenzato da altre attività, cache, ecc.).
  4. Cerca un gran numero di righe che entrano in un operatore, ma ne escono piccoli numeri. Di solito, un indice aiuterebbe limitando il numero di righe in arrivo (che salva le letture del disco).
  5. Concentrati prima sul sottostruttura di costo più grande. La modifica di tale sottostruttura può spesso modificare l'intero piano di query.
  6. I problemi comuni che ho visto sono:
    • Se ci sono molti join, a volte Sql Server sceglierà di espandere i join e quindi applicare le clausole WHERE. In genere è possibile risolvere questo problema spostando le condizioni WHERE nella clausola JOIN o una tabella derivata con le condizioni incorporate. Le viste possono causare gli stessi problemi.
    • Join non ottimali (LOOP vs HASH vs MERGE). La mia regola empirica è quella di utilizzare un join LOOP quando la riga superiore ha pochissime righe rispetto alla parte inferiore, un MERGE quando i set sono approssimativamente uguali e ordinati e un HASH per tutto il resto. L'aggiunta di un suggerimento di join ti consentirà di testare la tua teoria.
    • Sniffing dei parametri. Se all'inizio è stato eseguito il proc memorizzato con valori non realistici (ad esempio, per i test), il piano di query memorizzato nella cache potrebbe non essere ottimale per i valori di produzione. Eseguire di nuovo WITH RECOMPILE dovrebbe verificarlo. Per alcuni proc memorizzati, in particolare quelli che si occupano di intervalli di dimensioni variabili (diciamo, tutte le date tra oggi e ieri - che comporterebbe un INDICE SEEK - o, tutte le date tra l'anno scorso e quest'anno - che sarebbe meglio con una SCANSIONE INDICE ) potrebbe essere necessario eseguirlo CON RECOMPILE ogni volta.
    • Cattivo rientro ... Va bene, quindi Sql Server non ha problemi con questo - ma trovo sicuramente impossibile capire una query fino a quando non avrò corretto la formattazione.

1
+1 per l'inclusione di cattiva indentazione. La formattazione è la chiave! :)
mwigdahl,

18

Leggermente fuori tema, ma se si ha il controllo su questi problemi ...
Alto livello e alto impatto.

  • Per ambienti IO elevati, assicurarsi che i dischi siano per RAID 10 o RAID 0 + 1 o per un'implementazione nidificata del raid 1 e del raid 0.
  • Non utilizzare unità inferiori a 1500 K.
  • Assicurarsi che i dischi vengano utilizzati solo per il database. IE no logging no OS.
  • Disattiva la funzione di crescita automatica o simile. Consentire al database di utilizzare tutto l'archiviazione prevista. Non necessariamente ciò che viene attualmente utilizzato.
  • progettare lo schema e gli indici per le query di tipo.
  • se è una tabella dei tipi di registro (solo inserire) e deve trovarsi nel DB, non indicizzarla.
  • se stai eseguendo una serie di rapporti (il complesso seleziona con molti join), dovresti cercare di creare un data warehouse con uno schema a stella o fiocco di neve.
  • Non aver paura di replicare i dati in cambio di prestazioni!

8

CREATE INDEX

Assicurati che ci siano indici disponibili per la tua WHEREe JOINclausole. 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 DELETEoperazioni.)

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, idx01fornisce 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.


6

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


5

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 .


3
Va bene per MySQL, ma la domanda è stata taggata "sqlserver". Tuttavia, è una buona cosa farlo. L'analoga cosa da fare in SSMS è utilizzare "Visualizza piano di esecuzione stimato" e "Includi piano di esecuzione effettivo". Se riesci a eliminare enormi scansioni di tabelle e utilizzare ricerche di indici in cluster, sei sulla buona strada per prestazioni ottimali.
eksortso,

5

@Terrapin ci sono alcune altre differenze tra isnull e coalesce che vale la pena menzionare (oltre alla conformità ANSI, che è una grande per me).

Coalesce vs. IsNull


3

A volte in SQL Server se si utilizza un OR in una clausola where, le prestazioni aumenteranno notevolmente. Invece di usare l'OR basta fare due selezioni e unirle insieme. Ottieni gli stessi risultati a 1000 volte la velocità.


Ho visto questo comportamento inspiegabile.
Esen,

2

Guarda la clausola where - verifica l'uso degli indici / verifica che non sia stato fatto nulla di stupido

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

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.


2

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)
)

2

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.


2

Primo passo: guarda il Piano di esecuzione delle query!
TableScan -> bad
NestedLoop -> meh warning
TableScan dietro un NestedLoop -> DOOM!

SET STATISTICS IO ON
SET STATISTICS TIME ON


2

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.


2
Questo dovrebbe essere usato con giudizio, non abitualmente. Il bloccaggio non è male, è solo frainteso.

2

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

1

@ 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.


1

Indicizza la (e) tabella (e) per il clm (s) per cui filtri


1

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).


1

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.


1

Cerco:

  • Annullare eventuali loop CURSOR e convertirli in istruzioni UPDATE / INSERT basate su set.
  • Cerca qualsiasi codice dell'applicazione che:
    • Chiama un SP che restituisce un ampio set di record,
    • Quindi nell'applicazione, passa attraverso ogni record e chiama un SP con parametri per aggiornare i record.
    • Converti questo in un SP che fa tutto il lavoro in una transazione.
  • Qualsiasi SP che esegue molta manipolazione di stringhe. È la prova che i dati non sono strutturati correttamente / normalizzati.
  • Qualsiasi SP che reinventa la ruota.
  • Qualsiasi SP che non riesco a capire cosa sta cercando di fare in un minuto!

1
SET NOCOUNT ON

Di solito la prima riga all'interno delle mie procedure memorizzate, a meno che non sia effettivamente necessario utilizzare @@ROWCOUNT.


2
@@ ROWCOUNT è impostato comunque. NOCOUNT disabilita le istruzioni "Righe xx interessate".
Sklivvz,

Questo fa davvero una differenza apprezzabile nelle prestazioni?
JohnFx,

Sì, il conteggio non viene calcolato automaticamente ogni volta che viene eseguita un'istruzione SQL. È abbastanza semplice eseguire il banco di una query con e senza vedere che fa la differenza.
Travis,

Il conteggio viene comunque monitorato in SQL Server. Qualsiasi differenza di prestazione che vedi è perché i conteggi devono passare attraverso la rete al tuo front-end. Se stai facendo un singolo SELEZIONA non farà una differenza apprezzabile. Se hai un loop con 100000 inserti, c'è molto di più sulla rete.
Tom H,

1

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'

3
NOLOCK è solo per le domande per le quali non ti interessano i risultati corretti
Mark Sowul,

1

Rimuovere i cursori ovunque non siano necessari.


Sì, i cursori sono una maledizione! ;)
Sklivvz,

8
Ugh. Non buttarlo fuori non qualificato in quel modo. I cursori sono come pistole. Non sono cattivi da soli, è solo che le persone fanno cose davvero cattive con loro.
JohnFx,

1

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.


0
  • Prefisso tutte le tabelle con dbo. per prevenire ricompilazioni.
  • Visualizza piani di query e ricerca di scansioni di tabelle / indici.
  • Nel 2005, setacciare le viste di gestione per gli indici mancanti.


0

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.


1
Hai effettivamente confrontato questo? Se SQL Server sta facendo ciò che è ragionevole (usando un algoritmo hash per localizzare il Proc memorizzato), allora questo non farebbe alcuna differenza. In effetti, se SQL Server non lo facesse, sembra che le prestazioni del sistema potrebbero puzzare (dal momento che presumibilmente chiama i propri proc).
John Stauffer,

1
Penso che questo rientri nel campo dell'ottimizzazione prematura. Probabilmente è una buona pratica evitare confusione per le persone, ma come suggerimento per l'ottimizzazione ... D-
JohnFx

0

Letture sporche -

set transaction isolation level read uncommitted

Previene i dead dead in cui l'integrità transazionale non è assolutamente necessaria (che di solito è vera)


1
Sì, ma questo può portare a strani bug che sono MOLTO difficili da trovare.
Grant Johnson,

0

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.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.