2013-02-13 10 views
10

ho bisogno di conferma/spiegazione da voi pro/guru con la seguente perché la mia squadra mi sta dicendo "non ha importanza" e me :) sta fustratingSQL Performance, .Net Ottimizzazioni vs Best Practices

Sfondo : Abbiamo un SQL Server 2008 che viene utilizzato dalla nostra principale app Web MVC3/.Net4. Abbiamo circa 200 utenti simultanei in qualsiasi momento. Il server viene colpito in modo ESTREMAMENTE rigido (blocchi, timeout, lentezza generale) e sto cercando di applicare le cose che ho imparato durante la mia carriera e alla mia ultima certificazione MS. Sono cose su cui siamo stati addestrati ("chiudi connessioni SQL STAT") e sto cercando di spiegare alla mia squadra che queste "piccole cose", anche se non sono le uniche a fare la differenza, alla fine si aggiunge

ho bisogno di sapere se la seguente hanno un impatto sulle prestazioni o se è solo 'best practice'

1. Utilizzando "USO" parola chiave la maggior parte del loro codice è simile a questo:.

public string SomeMethod(string x, string y) { 
    SomethingDataContext dc = new SomethingDataContext(); 
    var x = dc.StoredProcedure(x, y); 
} 

Mentre sto cercando di dire loro che USING chiude/libera più velocemente le risorse:

using (SomethingDataContext dc = new SomethingDataContext()) { 
    var x = dc.StoredProcedure(x, y); 
} 

Il loro argomento è che il GC fa un buon lavoro di pulizia dopo che il codice è stato eseguito, quindi USING non ha un impatto enorme. Vero o falso e perché?

2. Pool di connessione

ho sempre sentito la creazione di pool di connessioni in grado di accelerare in modo significativo qualsiasi sito web (almeno .Net w/MSSQL). ho raccomandato aggiungiamo quanto segue per i nostri connectionStrings nel web.config:

... "Pooling = true; Min Pool Size = 3; Max Pool Size = 100; Connessione Timeout = 10;". ..

L'argomento è che .Net/MSSQL già imposta i pool di connessione dietro le quinte e non è necessario inserirli nel nostro web.config. Vero o falso? Perché ogni altro sito afferma che il pooling deve essere aggiunto per prestazioni ottimali se è già configurato?

3. Minimizzare # di chiamate al db

il provider di ruoli/appartenenza che viene fornito con il default del progetto .Net MVC è bello - è a portata di mano e fa la maggior parte del lavoro sporco per voi. Ma questi ragazzi stanno facendo un uso serio di UsersInRoles() e lo usano liberamente come una variabile globale (colpisce il DB ogni volta che viene chiamato questo metodo). Ho creato un "oggetto utente" che carica tutti i ruoli in anticipo su ogni pageload (insieme ad altri elementi utente, come GUID, ecc.) E quindi interrogare questo oggetto per se l'utente ha il ruolo.

Altre parti del sito Web hanno istruzioni FOR che eseguono il looping oltre 200 volte e fanno query di 20-30 sql su ogni pass = oltre 4.000 chiamate al database. In qualche modo lo fa in pochi secondi, ma quello che voglio fare è consolidare le chiamate da 20-30 DB in una, in modo che faccia una chiamata 200 volte (ogni ciclo). Ma poiché SQL Profiler dice che la query ha richiesto "0 secondi", l'argomento è che è così veloce e piccolo che i server possono gestire questo numero elevato di query DB.

Il mio pensiero è "sì, queste query sono in esecuzione veloce, ma stanno uccidendo le prestazioni generali del server SQL." Questo potrebbe essere un fattore che contribuisce? Sono preoccupato di nulla, o questo è un fattore (significativo) che contribuisce ai problemi di prestazioni generali del server?

4. Altre ottimizzazioni del codice

il primo che viene in mente è con StringBuilder vs una variabile stringa semplice. Capisco perché dovrei usare StringBuilder (specialmente nei loop), ma dicono che non importa - anche se hanno bisogno di scrivere linee 10k +, il loro argomento è che il guadagno di prestazioni non ha importanza.

Quindi, tutto sommato, sono tutte le cose che apprendiamo e ci hanno perforato ("riduci al minimo le possibilità!") Solo "best practice" senza guadagni di prestazioni reali o contribuiscono a un rendimento reale/misurabile perdita?

EDIT *** Grazie ragazzi per tutte le vostre risposte! Ho una nuova (quinta) domanda basata sulle tue risposte: In effetti non usano "USING", quindi cosa significa che sta succedendo? Se c'è un pool di connessioni che avviene automaticamente, sta vincolando le connessioni dal pool fino a quando il GC non arriva? È possibile che ogni connessione aperta al server SQL stia aggiungendo un po 'più di peso al server e rallentandolo?

Sulla base dei vostri suggerimenti, ho intenzione di fare un serio benchmarking/registrazione dei tempi di connessione perché ho il sospetto che a) il server sia lento, b) non stanno chiudendo le connessioni e c) Profiler sta dicendo che è in esecuzione 0 secondi, la lentezza potrebbe provenire dalla connessione.

Apprezzo molto il vostro aiuto ragazzi. Grazie ancora

+0

Per non dire che la ricerca non è importante, ma i guru di SO forniscono un sacco di informazioni ... studiare quello che dicono è molto ricerca –

+0

@ JakeWilson801 La documentazione da MS non è sempre la migliore fonte. (Modifica: o anche una buona fonte) –

+0

Riusando il generatore di stringhe invece di concatenare le stringhe - se si concatenano le stringhe una volta (cioè 'var s =" a "+" b "'), allora una stringa sarà più efficiente, cosa è necessario ricordare che l'istanza di una stringa non è mutabile, quindi in un ciclo es 'string s =" a "; for (int i = 1, i <1000; i ++) {s + = "a";} 'crei una nuova istanza di stringa per ogni ciclo, questo avrà un impatto sull'allocazione della memoria e influenzerà le prestazioni complessive (che sia significativo o no dipenderà dal numero di concatenazioni). – GarethD

risposta

5

Separare il codice, apportare le modifiche & benchmark + profilo rispetto alla base di codice corrente. Quindi avrai delle prove per eseguire il backup delle tue richieste.

Per quanto riguarda le vostre domande, ecco qui:

  1. Si deve sempre disporre manualmente delle classi che implementano IDisposable, il GC non sarà effettivamente chiamare smaltire se la classe implementa anche un finalizzatore allora chiamerà il finalizzatore tuttavia nella maggior parte delle implementazioni ripulisce solo risorse non gestite.

  2. È vero che il framework .NET già fa il pool di connessioni, non sono sicuro di quali siano i valori predefiniti ma i valori delle stringhe di connessione sarebbero lì solo per consentire di modificarli.

  3. Il tempo di esecuzione dell'istruzione SQL è solo una parte della storia, in SQL profiler tutto ciò che vedrete è quanto tempo il motore del database ha impiegato per eseguire la query, quello che vi manca è il tempo necessario web server per connettersi e ricevere i risultati dal server di database in modo che, mentre la query può essere veloce, è possibile risparmiare su un sacco di latenza di rete IO & tramite il batch delle query.

  4. Questo è un buon modo per eseguire alcuni profili per dimostrare la memoria aggiuntiva utilizzata dalla concatenazione sui costruttori di stringhe.

+1

Secondo [questo sito] (http://www.connectionstrings.com/articles/show/all-sql-server-connection-string-keywords), i valori predefiniti per il pool di connessioni sono 0 per min, 100 per max. –

+0

+1 Questa risposta è il miglior lavoro per affrontare a fondo ogni domanda. Mi piace anche il suggerimento di diramazione e benchmark. –

+0

Per il numero 1 - molti di voi hanno detto la stessa cosa - che il GC non lo ripulirà immediatamente. Quindi si lega uno dei pool di connessione? alcune centinaia di queste connessioni aperte potrebbero rallentare il server? Per il n. 3, mi hai appena dato un punto di discussione ENORME. Cercherò di isolare il tempo di connessione e aggiungerlo per ognuna delle oltre 2000 chiamate db per clic e vedere cosa ne viene fuori - buona idea grazie! – Losbear

3
  1. oggetti che implementano IDisposable e tenere sulle risorse inmanaged anche attuare un finilizer che farà in modo che Dispose viene chiamato durante GC, il problema è quando viene chiamato, il GC può prendere un sacco di tempo per fare e tu hai bisogno di quelle risorse prima di quello. L'uso effettua la chiamata a dispose non appena ne hai finito.

  2. è possibile modificare i parametri di mettere in comune in WebConfig, ma la sua attiva per impostazione predefinita, in modo che se si lascia i parametri di default non si è stanno guadagnando niente

  3. Non solo pensare a quanto tempo ci vuole la query da eseguire ma anche il tempo di connessione tra application server e database, anche se sullo stesso computer aggiunge un sovraccarico.

  4. StringBuilder non influisce sulle prestazioni nella maggior parte delle applicazioni Web, sarebbe importante solo se concatenate 2 volte alla stessa stringa, ma penso che sia una buona idea usarlo poiché è più facile da leggere.

+1

Inoltre: gli oggetti che hanno un finalizzatore (e per il quale non è stato chiamato SuppressFinalize) richiedono due garbage collection finché non vengono completamente rimossi. –

+0

In aggiunta al mio commento: i tipi che hanno un finalizzatore e implementano correttamente il modello monouso chiameranno SuppressFinalize nella loro funzione Dispose. Eliminando tali tipi, viene quindi rimossa la necessità della seconda raccolta dati inutili. (Questo dovrebbe riguardare il mio primo commento meglio alla domanda) –

1

Beh, io non sono un guru, ma io ho un suggerimento: se dicono che ti sbagli, dire loro, "Provalo Scrivimi una prova Dimostrami che 4000 le chiamate sono solo! veloce come 200 chiamate e ha lo stesso impatto sul server! "

Idem le altre cose. Se non sei in grado di dimostrare che hai ragione, prova che hanno torto, con test chiari e ben documentati che dimostrano che quello che stai dicendo è giusto.

Se non sono aperti anche a prove concrete, raccolte dal proprio server, con codice che possono guardare e ispezionare, allora si potrebbe perdere tempo in quella squadra.

+0

Non posso dire "Provalo" (per quanto mi piacerebbe) perché l'applicazione è stata scritta prima di salire a bordo. Quindi ora è il mio fardello dimostrare loro perché dovrebbero tornare indietro e correggere il loro codice :) – Losbear

+0

@Losbear Questa potrebbe essere una dura battaglia! Ma la direzione che hai detto (in altri commenti) che intendi inserire sembra buona. Prova e prova di nuovo e mostra loro numeri duri. Buona fortuna a te! –

0

La clausola usando è lo zucchero sintattico, si sono essenzialmente facendo

try 
{ 
    resouce.DoStuff(); 
} 
finally 
{ 
    resource.Dispose() 
} 

Smaltire è destinata probabilmente ad ottenere chiamato in ogni caso quando l'oggetto viene garbage collection, ma solo se i programmatori quadro ha fatto un buon lavoro di implementazione the disposable pattern. Quindi gli argomenti contro i tuoi colleghi sono:

i) se prendiamo l'abitudine di utilizzare utilizzando ci assicuriamo di liberare risorse non gestite perché non tutti i programmatori di framework sono intelligenti per implementare il modello monouso.

ii) sì, il GC alla fine pulirà quell'oggetto, ma potrebbe richiedere un po 'di tempo, a seconda di quanti anni ha quell'oggetto. Una pulizia GC di gen 2 viene eseguita solo una volta al secondo.

Così in breve:

  1. vedi sopra

  2. sì, il pool è impostato di default su true e massima dimensione del pool di 100

  3. lei ha ragione, sicuramente la zona migliore per spingere verso miglioramenti.

  4. l'ottimizzazione prematura è la radice di tutto il male. Ottieni prima il n. 1 e il n. Utilizzare il profiler SQL e i metodi specifici del database (aggiungere indici, deframmentarli, monitorare deadlock, ecc.).

  5. sì, potrebbe essere.il modo migliore è misurarlo: guarda il contatore perf SQLServer: Statistiche generali - Connessioni utente; here è un articolo che descrive come farlo.

Misurare sempre i miglioramenti, non modificare il codice senza prove!

+0

Concordato - l'unica cosa su cui io e il mio team sono d'accordo è che nessuno di noi vuole mettere troppo lavoro in qualcosa che non va per aiutare così tanto; come convertire variabili stringa in oggetti stringbuilder LOL. Penso che alla fine solo numeri difficili da profiler convinceranno questi ragazzi :) THanks Bogdan – Losbear

2

Penso che tu abbia due problemi separati qui.

  1. prestazioni del vostro codice
  2. prestazioni del database SQL Server

SQL Server

Avete qualche controllo sul posto per SQL Server? Sai in particolare quali query vengono eseguite che causano i deadlock?

Vorrei leggere this article on deadlocks e prendere in considerazione l'installazione del brillante Who is active per scoprire cosa sta realmente accadendo nel vostro SQL Server. Potresti anche considerare l'installazione di sp_Blitz da parte di Brent Ozar. Questo dovrebbe darti un'idea eccellente di cosa sta succedendo nel tuo database e fornirti gli strumenti per risolvere il problema prima.

Altro codice emette

non posso commentare le altre questioni di codice al largo della parte superiore della mia testa. Quindi guarderei prima il server SQL.

Ricordati

  1. Monitor
  2. identificare i problemi
  3. Profilo
  4. Fix
  5. Vai a 1
+0

Sì, ho guardato SQL Profiler sul server live e ho cercato di mostrare loro che "guarda, questo ONE ha fatto più di 2.000 chiamate !" ma la loro argomentazione è "ma ha fatto tutte quelle chiamate 2k in 2 secondi".Sto cercando di andare sopra e contrastarlo con "quei 2 secondi si sommano", ma non sono al 100% positivo, perché quei 2 secondi sono stati mescolati con altri 200 utenti simultanei. – Losbear

4

Oye. Di sicuro, non puoi consentire a GC di chiudere le connessioni del database per te. GC potrebbe non succedere per un lungo periodo ... a volte ore più tardi. Non succede subito non appena una variabile esce dal campo di applicazione. La maggior parte delle persone usa la sintassi IDisposable using() {}, che è grandiosa, ma per lo meno qualcosa, da qualche parte deve chiamare connection.Close()

0

Recentemente ho avuto a che fare con un bug nell'interazione tra il nostro web applicazione e il nostro provider di posta elettronica. Quando è stata inviata un'email, si è verificato un errore di protocollo. Ma non subito.

Sono stato in grado di determinare che l'errore si è verificato solo quando è stata chiusa l'istanza SmtpClient, che si verificava quando veniva smaltito lo SmtpClient, cosa che accadeva solo durante la garbage collection.

E ho notato che questo spesso ha preso due minuti dopo che il pulsante "Invia" è stato cliccato ...

manco a dirlo, il codice ora implementa correttamente using blocchi per entrambe le istanze SmtpClient e MailMessage.

Solo una parola al saggio ...

+0

2 minuti è meglio di quanto pensassi hehe - pensavo che il GC stesse girando ogni 5 o 10 minuti (il che è davvero pessimo). grazie per avermi dato un'idea (so che può essere + o- diversi minuti) di quanto spesso passa GC. – Losbear

0

1 è stato affrontato ben al di sopra (sono d'accordo con esso smaltimento bene, tuttavia, e ho trovato ad essere una buona pratica).

2 è un po 'un hold-over dalle versioni precedenti di ODBC in cui le connessioni di SQL Server sono state configurate indipendentemente per quanto riguarda il pooling. Era non predefinito; ora è predefinito.

Come 3 e 4, 4 non influenzerà le prestazioni del server SQL - StringBuilder potrebbe aiutare a velocizzare il processo all'interno dell'interfaccia utente, certamente, che potrebbe avere l'effetto di chiudere le risorse SQL più velocemente, ma hanno vinto ridurre il carico su SQL Server.

3 suona come il luogo più logico per concentrarmi, per me. Cerco di chiudere le mie connessioni al database il più rapidamente possibile e di fare il minor numero possibile di chiamate. Se stai usando LINQ, carica tutto in un IQueryable o qualcosa (lista, array, qualunque cosa) in modo che tu possa manipolarlo. & costruisci qualsiasi struttura UI di cui hai bisogno, mentre rilasci la connessione prima di una qualsiasi di quell'hokum.

Tutto ciò detto, sembra che sia necessario passare un po 'di tempo con il profiler. Piuttosto che osservare il tempo impiegato da ciascuna esecuzione, esaminare l'utilizzo della memoria del processore &. Solo perché sono veloci non significa che non siano esecuzioni "affamate".

1

A costo di solo ripetere ciò che altri qui hanno detto, ecco il mio 2c sulla questione

In primo luogo, si dovrebbe scegliere con attenzione le tue battaglie ... non vorrei andare in guerra con i tuoi colleghi su tutti i 4 punti perché non appena riesci a provare uno di loro, è finita, e dal loro punto di vista hanno ragione e ti sbagli. Ricorda inoltre che a nessuno piace essere detto che il loro bel codice è un bambino brutto, quindi presumo che sarai diplomatico - non dire "questo è lento", dì "Ho trovato un modo per rendere questo anche più veloce ".... (ovviamente la tua squadra potrebbe essere perfettamente ragionevole, quindi sto basandomi anche sulla mia esperienza :) Quindi devi scegliere una delle 4 aree qui sopra per affrontare per prima.

I miei soldi sono al numero 3. 1, 2 e 4 possono fare la differenza, ma nella mia esperienza personale, non così tanto - ma quello che hai descritto in # 3 suona come la morte di mille papercuts per il povero vecchio server! Le query probabilmente vengono eseguite velocemente perché sono parametrizzate in modo da essere memorizzate nella cache, ma è necessario tenere presente che "0 secondi" nel profiler potrebbero essere 900 millisecondi, se vedi cosa intendo ... aggiungilo per molti e le cose iniziano a rallentare; questa potrebbe anche essere una fonte primaria dei blocchi perché se ognuna di queste query annidate colpisce ripetutamente la stessa tabella, non importa quanto velocemente funzioni, con il numero di utenti che hai citato, è certo che avrai contese. Prendi l'SQL ed eseguilo in SSMS ma includi le Statistiche cliente in modo che tu possa vedere non solo il tempo di esecuzione ma anche la quantità di dati che vengono inviati al client; ciò ti fornirà un'immagine più chiara di quale tipo di sovraccarico è coinvolto.

In realtà, l'unico modo per dimostrare tutto questo è impostare un test e misurare come altri hanno menzionato, ma anche assicurarsi di eseguire anche alcuni profili sul server: blocchi, code di I/O, ecc. puoi dimostrare che non solo la tua strada è più veloce, ma che carica meno sul server.

Per toccare la quinta domanda - Non sono sicuro, ma suppongo che qualsiasi SqlConnection che non è auto-disposta (tramite l'utilizzo) viene conteggiata come ancora "attiva" e non è più disponibile dal pool.Detto questo, il sovraccarico della connessione è piuttosto basso sul server, a meno che la connessione non stia effettivamente facendo nulla, ma è possibile provarlo nuovamente utilizzando i contatori SQL Performance.

Buona fortuna, non vedo l'ora di scoprire come vai.

+0

Grazie Stephen. Sì, ho intenzione di essere diplomatico al riguardo, LOL :) Sospetto anche il n. 3, ma credo che mi stavo chiedendo quale tipo di impatto avrebbero avuto tutte quelle questioni collettivamente - o è il problema UNICO che potrebbe causare una lentezza generale . Grazie ancora per il tuo 2c - pubblicherò i miei risultati quando eseguirò i numeri domani :) – Losbear

Problemi correlati