Applicazione che richiede tabelle vuote


10

La mia azienda utilizza un'applicazione che presenta problemi di prestazioni piuttosto importanti. Ci sono una serie di problemi con il database stesso su cui sto lavorando, ma molti di questi sono puramente correlati all'applicazione.

Nella mia indagine ho scoperto che ci sono milioni di query che colpiscono il database di SQL Server che interrogano tabelle vuote. Abbiamo circa 300 tabelle vuote e alcune di queste tabelle vengono interrogate fino a 100-200 volte al minuto. Le tabelle non hanno nulla a che fare con la nostra area di business e sono essenzialmente parti dell'applicazione originale che il fornitore non ha rimosso quando sono stati contratti dalla mia azienda per produrre una soluzione software per noi.

A parte il fatto che sospettiamo che il nostro registro degli errori dell'applicazione sia inondato da errori relativi a questo problema, il fornitore ci assicura che non vi è alcun impatto sulle prestazioni o sulla stabilità né per l'applicazione né per il server di database. Il registro degli errori è inondato nella misura in cui non possiamo vedere più di 2 minuti di errori per fare diagnosi.

Il costo effettivo di queste query sarà ovviamente basso in termini di cicli della CPU, ecc. Ma qualcuno può suggerire quale sarebbe l'effetto su SQL Server e l'applicazione? Sospetterei che i meccanismi effettivi dell'invio di una richiesta, della conferma, dell'elaborazione, della restituzione e del riconoscimento della ricevuta da parte dell'applicazione avrebbero un impatto sulle prestazioni.

Usiamo SQL Server 2008 R2, Oracle Weblogic 11g per l'app.

@ Frisbee- Per farla breve, ho creato una tabella contenente il testo della query che ha colpito le tabelle vuote nel database dell'app, quindi l'ho interrogato per tutti i nomi di tab che conosco vuoti e ho ottenuto un elenco molto lungo. Il massimo successo è stato di 2,7 milioni di esecuzioni in 30 giorni di attività, tenendo presente che l'app è generalmente in uso dalle 8:00 alle 18:00, quindi questi numeri sono più concentrati sulle ore operative. Più tabelle, più query, probabilmente alcuni relavent tramite join, altri no. Il colpo più alto (2,7 milioni all'epoca) fu una semplice selezione da una singola tabella vuota con una clausola where, senza join. Mi aspetto che query più grandi con join alle tabelle vuote possano includere aggiornamenti alle tabelle collegate, ma lo controllerò e aggiornerò questa domanda al più presto.

Aggiornamento: ci sono 1000 query con un numero di esecuzioni compreso tra 1043 e 4622614 (oltre 2,5 mesi). Dovrò scavare di più per scoprire da dove proviene il piano memorizzato nella cache. Questo è solo per darti un'idea dell'entità delle query. La maggior parte sono ragionevolmente complessi con oltre 20 join.

@ srutzky- sì, credo che ci sia una colonna di date relativa a quando il piano è stato compilato in modo che possa essere interessante, quindi lo controllerò. Mi chiedo che i limiti del thread possano essere un fattore quando SQL Server si trova su un cluster VMware? Presto sarà un Dell PE 730xD dedicato per fortuna.

@Frisbee - Ci scusiamo per la risposta in ritardo. Come hai suggerito, ho eseguito un select * dalla tabella vuota 10.000 volte su 24 thread utilizzando SQLQueryStress (quindi in realtà 240.000 iterazioni) e ho risposto immediatamente a 10.000 richieste batch / sec. Poi ho ridotto a 1000 volte su 24 thread e ho raggiunto poco meno di 4.000 richieste batch / sec. Ho anche provato 10.000 iterazioni su solo 12 thread (quindi 120000 iterazioni totali) e questo ha prodotto 6.505 batch al secondo. L'effetto sulla CPU è stato in realtà evidente, circa il 5-10% dell'utilizzo totale della CPU durante ogni test. Le attese di rete erano trascurabili (come 3ms con il client sulla mia workstation) ma l'impatto della CPU era sicuramente lì, il che è abbastanza conclusivo per quanto mi riguarda. Sembra ridursi all'uso della CPU e un po 'di inutili file di database IO. Le esecuzioni / secondo totali si attestano a poco meno di 3000, che è più che in produzione, tuttavia sto testando solo una delle dozzine di domande come questa. L'effetto netto di centinaia di query che colpiscono tabelle vuote a una velocità compresa tra 300-4000 volte al minuto non sarebbe quindi trascurabile quando si tratta del tempo della CPU. Tutti i test eseguiti su un PE 730xD inattivo con doppio array di flash e 256 GB di RAM, 12 core moderni. Questo è l'output di SQLSentry

@ srutzky- buona riflessione. SQLQueryStress sembra utilizzare il pool di connessioni per impostazione predefinita, ma ho comunque dato un'occhiata e ho scoperto che sì, la casella per il pool di connessioni è selezionata. Aggiornamento da seguire

@ srutzky- Il pool di connessioni non è apparentemente abilitato sull'applicazione o, in caso affermativo, non funziona. Ho eseguito una traccia del profiler e ho scoperto che le connessioni hanno EventSubClass "1 - Non pool" per gli eventi di accesso di controllo.

RE: Pool di connessioni - Controllato i weblogics e trovato pool di connessioni abilitato. Ho tracciato più tracce contro i segni di pool e live non trovati correttamente / affatto: inserisci qui la descrizione dell'immagine

Ed ecco come appare quando eseguo una singola query senza join su una tabella popolata; le eccezioni recitavano "Si è verificato un errore di rete o specifico dell'istanza durante la creazione di una connessione a SQL Server. Il server non è stato trovato o non era accessibile. Verificare che il nome dell'istanza sia corretto e che SQL Server sia configurato per consentire connessioni remote. (provider: Named Pipes Provider, errore: 40 - Impossibile aprire una connessione a SQL Server) "Nota il contatore delle richieste batch. Il ping del server durante il periodo in cui vengono generate le eccezioni provoca una risposta ping riuscita.

inserisci qui la descrizione dell'immagine

Aggiornamento: due esecuzioni di test consecutive, stesso carico di lavoro (selezionare * dalla tabella di vuoto), pooling abilitato / non abilitato. Utilizzo della CPU leggermente maggiore e molti errori e non supera mai le 500 richieste batch / sec. I test mostrano 10.000 lotti / sec e nessun errore con pool attivo, e circa 400 batch / sec, quindi molti errori dovuti alla disabilitazione del pool. Mi chiedo se questi errori sono correlati a una mancanza di disponibilità della connessione?

inserisci qui la descrizione dell'immagine

@ srutzky- Seleziona Count (*) da sys.dm_exec_connections;

  • Pooling abilitato: 37 in modo coerente, anche dopo l'arresto del test di carico

  • Pooling disabilitato: 11-37 a seconda che si
    verifichino o meno eccezioni su SQLQueryStress, ovvero: quando tali trogoli vengono visualizzati nel
    grafico Batch / sec, le eccezioni si verificano su SQLQueryStress e il
    numero di connessioni scende a 11, quindi si ripristina gradualmente fino a 37 quando i batch iniziano a raggiungere il picco e non si verificano eccezioni. Molto, molto interessante.

Numero massimo di connessioni su entrambe le istanze di test / live impostate sul valore predefinito di 0.

Sono stati controllati i registri dell'applicazione e non sono stati rilevati problemi di connettività, tuttavia sono disponibili solo un paio di minuti di registrazione a causa dell'elevato numero e dimensione degli errori, ovvero: molti errori di tracciabilità dello stack. Un collega sul supporto dell'app avvisa che si verifica un numero considerevole di errori HTTP relativi alla connettività. Sembrerebbe basato su questo, che per qualche ragione l'applicazione non stia raggruppando correttamente le connessioni e, di conseguenza, il server sta esaurendo ripetutamente le connessioni. Esaminerò di più i registri delle app. Mi chiedo c'è un modo per dimostrare che ciò sta accadendo nella produzione dal lato SQL Server?

@ srutzky- Grazie. Domani controllerò la configurazione della weblogic e aggiornerò. Stavo pensando però alle sole 37 connessioni: se SQLQueryStress sta eseguendo 12 thread a 10.000 iterazioni = 120.000 istruzioni select non raggruppate, non dovrebbe significare che ogni selezione crea una connessione distinta all'istanza sql?

@ srutzky- Le weblogics sono configurate per il pool di connessioni, quindi dovrebbe funzionare correttamente. Il pool di connessioni è configurato in questo modo, su ciascuno dei 4 weblogics con bilanciamento del carico:

  • Capacità iniziale: 10
  • Capacità massima: 50
  • Capacità minima: 5

Quando aumento il numero di thread che eseguono la query di selezione da tabella vuota, il numero di connessioni raggiunge un picco di circa 47. Con il pool di connessioni disabilitato, vedo costantemente un numero massimo di richieste batch al secondo (da 10.000 a circa 400). Ciò che accadrà ogni volta è che le "eccezioni" su SQLQueryStress si verificano poco dopo che i batch / sec entrano in un trogolo. È legato alla connettività ma non riesco a capire esattamente perché questo accada. Quando nessun test è in esecuzione, #connections scende a circa 12.

Con il pool di connessioni disabilitato, non riesco a capire perché si verificano le eccezioni, ma forse si tratta di un'intera altra pila Scambio domanda / domanda per Adam Machanic?

@srutzky Mi chiedo quindi perché si verificano le eccezioni senza il pooling abilitato, anche se SQL Server non sta esaurendo le connessioni?


1
Peter, tenendo conto degli aggiornamenti più recenti relativi al pool di connessioni, sembra che ora sia necessario rieseguire i test con SQLQueryStress ma con Pool di connessioni disattivato . Sarebbe un riflesso più accurato degli effetti del funzionamento dell'app e credo che mostrerà un aumento dell'utilizzo della CPU e persino dell'utilizzo della RAM.
Solomon Rutzky,

1
Peter, hai impostato un numero massimo di connessioni per il server? Immagino che senza il pool stai incontrando un problema di troppe connessioni. Mi chiedo se la tua app abbia mai avuto quell'errore. Inoltre, se possibile rieseguire l'ultimo test ancora una volta (sia con che senza pooling abilitato), mentre il test è in esecuzione per ognuna di queste due configurazioni, eseguire a SELECT COUNT(*) FROM sys.dm_exec_connections;per vedere se il valore è molto diverso tra il pooling abilitato o non. Sulla base di questi errori, penso che ci sarebbero molte più connessioni quando il pooling è disabilitato.
Solomon Rutzky,

1
Peter, 37 connessioni sembrano un massimo terribilmente basso. Dato che il limite di connessione è impostato su 0 (cioè illimitato), la memoria di sistema è associata? Inoltre, il pool di connessioni dovrebbe essere attivo per impostazione predefinita, ma è controllato dal client. L'app è un'app .NET? Non è necessario essere per utilizzare il pool di connessioni, ma sarebbe utile sapere per trovare la causa. E vedi quale stringa di connessione viene utilizzata? Specifica Pooling=falseo Max Pool Size?
Solomon Rutzky,

1
Peter, ciascuno dei 12 thread sta creando la propria connessione per query, in sequenza per le iterazioni 10k. Quindi, senza pool, la connessione può essere distrutta non appena il codice chiude la connessione. Il pool manterrà la connessione per il riutilizzo. Quindi ha senso che il numero di connessioni fosse coerente durante l'utilizzo del pool. Non sono sicuro del perché 37 senza ulteriori informazioni. Quante connessioni ci sono quando nessun test è in esecuzione? Il ripristino di quel numero fornirà una migliore indicazione di quanti sono stati creati dal test.
Solomon Rutzky,

1
Il pool di connessioni viene gestito per client, non per server. Quindi WebLogics e SQLQueryStress dovrebbero avere i propri pool di connessioni (in termini di dimensioni min_pool e max_pool, ecc.). Per quanto riguarda "Con pool di connessioni disabilitato, vedo un numero massimo di richieste batch al secondo": questo ha senso poiché richiede più tempo per ogni connessione dall'app per autenticare e inizializzare la sessione, ecc. Ecco perché esiste il pool di connessioni: - ).
Solomon Rutzky,

Risposte:


7

Sospetterei che i meccanismi effettivi dell'invio di una richiesta, della conferma, dell'elaborazione, della restituzione e del riconoscimento della ricevuta da parte dell'applicazione avrebbero un impatto sulle prestazioni.

Sì, e ci sono anche alcuni fattori aggiuntivi, ma il grado in cui uno di questi sta effettivamente influenzando il tuo sistema è impossibile da dire senza analizzarlo.

Detto questo, stai chiedendo quale potrebbe essere un problema, e ci sono alcune cose da menzionare, anche se alcune di queste non sono attualmente un fattore nella tua situazione particolare. Lo dici tu:

Abbiamo circa 300 tabelle vuote e alcune di queste tabelle vengono interrogate fino a 100-200 volte al minuto.

  • Le tabelle vuote che non vengono interrogate non sono un problema. Ma suppongo che potresti anche voler dire che vengono tutti interrogati, solo che alcuni vengono colpiti molto più di altri.
  • L'analisi delle query e la generazione del piano di esecuzione non dovrebbero costituire un grosso problema se il testo della query inviato rimane lo stesso per tutte le chiamate. SQL Server eseguirà l'hashing del testo della query e lo cercherà nella cache del piano. Se trovato, non eseguirà più i passaggi di analisi o compilazione (fino a quando il piano non viene rimosso dalla cache).
  • Qualsiasi tabella, vuota o non vuota, richiederà almeno un blocco "condiviso" per indicare che la risorsa è in uso. Ciò impedisce alle operazioni che richiedono blocchi esclusivi (aggiungi / modifica / rimuovi colonne, ecc.) Di apportare le modifiche mentre la risorsa è in uso. Il blocco e lo sblocco, anche se eseguiti in meno di 1 millisecondo in assenza di dati, richiedono ancora risorse di sistema (memoria e CPU) per gestire tali operazioni di blocco.
  • Anche senza set di risultati che tornano all'app da SQL Server, c'è sempre la stessa quantità di traffico di rete diretto a SQL Server indipendentemente dal fatto che la query produca risultati o meno. È necessario inviare il testo della query o il nome della procedura memorizzata. E anche se non viene restituito alcun risultato, SQL Server deve comunque inviare alcuni pacchetti di rete contenenti la struttura del set di risultati oltre ai pacchetti che indicano al client che sta iniziando un set di risultati (anche se non viene trovata alcuna riga) e che il set di risultati è termina e dovrebbe essere chiuso. E potrebbero esserci messaggi aggiuntivi dalle dichiarazioni di stampa e / o dal conteggio delle righe.
  • La connessione a SQL Server richiede una certa quantità di risorse di sistema. Ci vuole CPU e memoria per gestire l'autenticazione (così come i pacchetti di rete avanti e indietro) e anche questo richiede tempo. Questo è il motivo per cui esiste un pool di connessioni: ridurre questa spesa.
  • Anche con il pool di connessioni che riduce l'utilizzo delle risorse di sistema, SQL Server deve comunque mantenere tali connessioni e ciò richiede memoria e CPU minima.
  • Anche senza righe e quindi con tempi di esecuzione molto rapidi, la query è stata comunque eseguita. Anche se c'erano 10 o 10.000 righe e quelle venivano estratte dal pool di buffer (cioè memoria) poiché venivano usate frequentemente, un thread deve ancora fare quel lavoro. E un thread che sta lavorando su questa query inutile non funziona su una query utile effettiva.

Potrebbero essercene ancora di più, ma questo dovrebbe aiutare a capire le cose. E tieni presente che, come la maggior parte dei problemi di prestazioni, è tutta una questione di scala. Tutti gli elementi sopra menzionati non sono problematici se colpiti una volta al minuto. È come testare una modifica sulla workstation o nel database di sviluppo: funziona sempre con solo 10 - 100 righe nelle tabelle. Sposta quel codice in produzione e ci vogliono 10 minuti per essere eseguito, e qualcuno è destinato a dire: "beh, funziona sulla mia scatola" ;-). Ciò significa che è solo a causa dell'enorme volume di chiamate che si sta riscontrando un problema, ma questa è la situazione esistente.

Quindi, anche con 1 milione di query inutili a 0 righe, ciò equivale a:

  • ulteriori 2 milioni di operazioni di blocco (ogni blocco deve essere sbloccato, giusto?). questo è principalmente un costo del tempo speso per un'operazione inutile invece che per un'operazione utile.
  • più traffico di rete che potrebbe avvicinarti alla saturazione (non sono sicuro di quanto sia probabile, ma comunque)
  • vengono mantenute più connessioni che occupano più memoria. Quanta RAM fisica non utilizzata hai? tale memoria sarebbe meglio utilizzata per l'esecuzione di query e / o cache del piano di query. Il caso peggiore sarebbe che la memoria fisica sia esaurita e che SQL Server debba iniziare a utilizzare la memoria virtuale (scambio), poiché ciò rallenta le cose (controllare il registro degli errori di SQL Server per vedere se si stanno ricevendo messaggi sul paging della memoria).

    E nel caso in cui qualcuno menzioni "bene, c'è un pool di connessioni". Sì, questo aiuta sicuramente a ridurre il numero di connessioni necessarie. Ma con le richieste che arrivano fino a 200 volte al minuto, sono molte le attività simultanee e le connessioni devono ancora esistere per le richieste legittime. Fai un SELECT * FROM sys.dm_exec_connections;per vedere quante connessioni attive stai mantenendo.

  • a prescindere da qualsiasi altra cosa, questo è ancora almeno 1 milione di volte ogni giorno che un thread che avrebbe potuto fare qualcosa di utile non era invece disponibile.

Se non sono errato su ciò che ho affermato qui, allora mi sembra che, anche se su piccola scala, questo è un tipo di attacco DDoS sul tuo sistema poiché sta inondando la rete e il tuo SQL Server con richieste fasulle , impedendo a richieste reali di arrivare a SQL Server o di essere elaborate da SQL Server.


1

Se i tavoli vengono colpiti 100-200 volte al minuto, sono (si spera) in memoria. Il carico sul server è molto basso. A meno che non si disponga di CPU o memoria elevate sul server di database, questo è probabilmente un problema.

Sì, le query accettano blocchi condivisi, ma si spera che non blocchino alcun blocco degli aggiornamenti né vengano bloccati da alcun blocco degli aggiornamenti. Hai qualche aggiornamento, inserimento o eliminazione su queste tabelle? Altrimenti lascerei perdere - se si verificano problemi di prestazioni, deve esserci un pesce più grande da friggere dal punto di vista del server di database.

Ho eseguito un test su 100.000 selezioni (*) su una tabella vuota ed è stato eseguito in 32 secondi e le query erano in rete. Quindi 1/3 millisecondi. A meno che la rete non venga sovraccaricata, ciò non influisce nemmeno sul client. Se riscontri importanti problemi di prestazioni, queste query vuote da 1/3 di millisecondo non sono ciò che sta uccidendo l'app.

E questi potrebbero essere solo parte di un join sinistro che afferra alcuni dati di tipo statico che non fanno parte dell'applicazione corrente. Potrebbe essere concatenato con altre query, quindi non è un viaggio di andata e ritorno. In tal caso sì, è sciatto ma non sta nemmeno causando più traffico.

Quindi torniamo a guardare le dichiarazioni reali. Stai vedendo aggiornamenti, aggiunte o eliminazioni su queste tabelle?

Sì, molte tabelle vuote e query a tabelle vuote sono indicazioni di codifica sciatta. Ma se stai riscontrando importanti problemi di prestazioni, questa non è la causa, a meno che tu non abbia alcune operazioni di scrittura davvero sciatte in corso con queste tabelle.


Quanti altri utenti stavano eseguendo query su SQL Server quando hai eseguito il test delle query 100k? Non sto dicendo che ho ragione e tu hai torto, ma se tu fossi l'unico sul sistema, o uno dei pochi, allora naturalmente non vedresti un grande impatto. Il problema del blocco non era una questione di blocco, era semplicemente una questione di risorse necessarie a SQL Server per bloccare e sbloccare quelle pagine di dati, anche se sono sempre nel pool di buffer. È ancora il lavoro che si sta facendo. E gli scheduler non sono illimitati.
Solomon Rutzky,

E non sto dicendo che ti sbagli. Altri utenti o meno è ancora una misura valida di quanto tempo ha impiegato e una misura di risorse Il carico dichiarato è 100-200 al minuto. 100.000 da un client in 30 secondi supera il carico di un fattore compreso tra 200 e 400. Se non ci sono blocchi di aggiornamento, se proviene da un client o 100 non fa differenza. La tua risposta presuppone che esista una rete sovraccarica o un server SQL e in base alla domanda che non conosci. Se si trattasse di un attacco DDoS, ci sarebbero più o meno 100 / sec (non minuti) e non sarebbe contro un tavolo vuoto.
paparazzo,

Corretto, in base alla domanda che non conosciamo abbastanza per restringerla, motivo per cui stavo dicendo che queste cose potrebbero essere un problema, a seconda delle circostanze. E la cosa DDoS era solo un'analogia, basata principalmente sulla formulazione della domanda originale che implicava che molti venivano colpiti a quel ritmo e molti altri venivano colpiti, anche meno frequentemente.
Solomon Rutzky,

Ritengo che questa sia una risposta preziosa, nel senso che il primo paragrafo lo riassume molto bene: "A meno che non si disponga di CPU o memoria elevate sul server di database, questo è probabilmente un problema." Nel nostro caso, abbiamo un elevato utilizzo della CPU in determinate ore del giorno e quindi la pressione aggiuntiva della CPU sembra essere un fattore basato sui miei test.
Peter,

In particolare ho citato solo query che eseguono 100-200 volte / minuto, quando in realtà ci sono circa 50 query su queste tabelle vuote con conteggi di esecuzione compresi tra 200-4000 / minuto. Cumulativamente, l'effetto di interrogare tabelle vuote con questa frequenza influisce parecchio sulla CPU, anche nel migliore dei casi in cui le query non parametrizzate vengono eseguite ripetutamente, quindi il piano, i dati ecc. Sono tutti in memoria.
Peter,

0

In generale su ogni query vengono eseguiti i seguenti passaggi:

  1. Richiesta dall'applicazione.
  2. Database Analizza la query.
  3. Motore di database verifica se questa query è già memorizzata nella RAM. usa il piano di esecuzione se esiste in memoria.
  4. se non esiste nella RAM, il motore di database controlla le statistiche esistenti sugli oggetti nella query e determina il piano di esecuzione.
  5. Esegui il piano di esecuzione, usa i / o per ottenere dati dal disco.
  6. risposta all'applicazione.

molte domande come hai menzionato potrebbero causare un carico extra su un sistema che è già pesante - carico extra su connessioni, CPU, RAM e I / O.

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.