Come posso risolvere un problema del pool di connessioni tra ASP.NET e SQL Server?


211

Negli ultimi giorni vediamo questo messaggio di errore nel nostro sito Web troppo:

"Timeout scaduto. Il periodo di timeout è trascorso prima di ottenere una connessione dal pool. Ciò potrebbe essersi verificato perché tutte le connessioni del pool erano in uso e la dimensione massima del pool era stata raggiunta."

Non abbiamo cambiato nulla nel nostro codice da un po '. Ho rivisto il codice per verificare le connessioni aperte che non si chiudevano, ma ho trovato che tutto andasse bene.

  • Come posso risolvere questo?

  • Devo modificare questo pool?

  • Come posso modificare il numero massimo di connessioni di questo pool?

  • Qual è il valore raccomandato per un sito Web ad alto traffico?


Aggiornare:

Devo modificare qualcosa in IIS?

Aggiornare:

Ho scoperto che il numero di connessioni attive è compreso tra 15 e 31 e ho riscontrato che il numero massimo consentito di connessioni configurate nel server SQL è superiore a 3200 connessioni, è 31 in eccesso o dovrei modificare qualcosa nella configrazione ASP.NET ?


L'impostazione predefinita Dimensione massima pool è 100 se ricordo correttamente. La maggior parte dei siti Web non utilizza più di 50 connessioni sotto carico pesante - dipende dal tempo necessario per il completamento delle query. Correzione a breve termine nella stringa di connessione: prova a impostare un valore più elevato nelle stringhe di connessione: "Dimensione massima pool = ..."
splattne

quanto? renderlo 200 per esempio?
Amr Elgarhy,

3
Penso che dovresti davvero cercare ciò che sta causando il problema. Le tue query (o alcune) sono molto lunghe?
splattne,

forse è questo il vero motivo, una domanda che sta impiegando molto tempo per essere eseguita, lo
cercherò

1
Spero che tu possa trovare il problema. Un suggerimento: se si utilizza SQL Server, provare "SQL Profiler" e cercare query lunghe: sql-server-performance.com/articles/per/…
splattne

Risposte:


218

Nella maggior parte dei casi i problemi di pool di connessioni sono correlati a "perdite di connessione". Probabilmente l'applicazione non chiude le connessioni al database in modo corretto e coerente. Quando si lasciano aperte le connessioni, rimangono bloccate fino a quando il Garbage Collector .NET non le chiude per sé chiamando il loro Finalize()metodo.

Vuoi assicurarti di chiudere davvero la connessione . Ad esempio, il codice seguente causerà una perdita di connessione, se il codice tra .Opene Closegenera un'eccezione:

var connection = new SqlConnection(connectionString);
connection.Open();
// some code
connection.Close();                

Il modo corretto sarebbe questo:

var connection = new SqlConnection(ConnectionString);
try
{
     connection.Open();
     someCall (connection);
}
finally
{
     connection.Close();                
}

o

using (SqlConnection connection = new SqlConnection(connectionString))
{
     connection.Open();
     someCall(connection);
}

Quando la funzione restituisce una connessione da un metodo di classe, assicurati di memorizzarla nella cache locale e di chiamarne il Closemetodo. Perderai una connessione usando questo codice per esempio:

var command = new OleDbCommand(someUpdateQuery, getConnection());
result = command.ExecuteNonQuery();
connection().Close(); 

La connessione restituita dalla prima chiamata a getConnection()non viene chiusa. Invece di chiudere la connessione, questa linea ne crea una nuova e cerca di chiuderla.

Se usi SqlDataReadero a OleDbDataReader, chiudili. Anche se chiudere la connessione stessa sembra fare il trucco, sforzati di chiudere esplicitamente gli oggetti del tuo lettore di dati quando li usi.


Questo articolo " Perché un pool di connessioni trabocca? " Dalla rivista MSDN / SQL spiega molti dettagli e suggerisce alcune strategie di debug:

  • Esegui sp_whoo sp_who2. Queste procedure memorizzate dal sysprocessessistema restituiscono informazioni dalla tabella di sistema che mostra lo stato e le informazioni su tutti i processi lavorativi. In genere, vedrai un ID processo server (SPID) per connessione. Se la connessione è stata denominata utilizzando l'argomento Nome applicazione nella stringa di connessione, sarà facile trovare le connessioni funzionanti.
  • Utilizzare SQL Server Profiler con il TSQL_Replaymodello SQLProfiler per tracciare connessioni aperte. Se hai familiarità con Profiler, questo metodo è più semplice del polling utilizzando sp_who.
  • Utilizzare Performance Monitor per monitorare i pool e le connessioni. Discuterò questo metodo tra un momento.
  • Monitorare i contatori delle prestazioni nel codice. È possibile monitorare l'integrità del pool di connessioni e il numero di connessioni stabilite utilizzando le routine per estrarre i contatori o utilizzando i nuovi controlli .NET PerformanceCounter.

4
Una piccola correzione: il GC non chiama mai il metodo Dispose di un oggetto, ma solo il suo finalizzatore (se ce n'è uno). Il finalizzatore può quindi effettuare una chiamata di "fallback" a Dispose, se necessario, anche se non sono sicuro che SqlConnection lo faccia.
Luca

1
C'è qualche tipo di degrado delle prestazioni quando dobbiamo impostare la dimensione massima del pool 50 e abbiamo solo pochi utenti.
Mahesh Sharma,

37

Al momento dell'installazione di .NET Framework v4.6.1, le nostre connessioni a un database remoto hanno immediatamente iniziato il timeout a causa di questa modifica .

Per risolvere è sufficiente aggiungere il parametro TransparentNetworkIPResolutionnella stringa di connessione e impostarlo su false :

Server = myservername; Database = MyDatabase; Trusted_Connection = true; TransparentNetworkIPResolution = false


In questo caso il problema è "Il periodo di timeout è trascorso prima di ottenere una connessione dal pool". La correzione della stringa di connessione è stata risolta o si è trattato di un problema di handshaking separato?
FBryant87,

1
Da quello che ricordo era esattamente lo stesso messaggio di errore della domanda. Si è verificato immediatamente dopo l'aggiornamento a .NET Framework v4.6.1.
ajbeaven

Questo è stato il caso anche per me, risolto per me su un servizio app in esecuzione su Azure, connessione a un database SQL di Azure. Stavo usando Dapper e smaltivo correttamente le connessioni, ma ho ancora ricevuto il messaggio di errore "Tempo scaduto prima di ottenere una connessione dal pool". Ma non di più, quindi grazie @ajbeaven
Steve Kennaird il

13

A meno che il tuo utilizzo non sia aumentato molto, sembra improbabile che ci sia solo un arretrato di lavoro. IMO, l'opzione più probabile è che qualcosa stia usando le connessioni e non le rilasci prontamente. Sei sicuro di usare usingin tutti i casi? O (attraverso qualunque meccanismo) rilasciando le connessioni?


13

Hai controllato DataReader che non sono chiusi e response.redirects prima di chiudere la connessione o un datareader. Le connessioni rimangono aperte quando non le si chiude prima di un reindirizzamento.


3
+1 - O funzioni che restituiscono DataReader - La connessione non si chiuderà mai al di fuori della funzione che hai creato ...
splattne

1
Se la tua funzione restituisce SqlDataReader, è meglio convertirla in DataTable piuttosto che aumentare la dimensione massima del pool
live-love

10

Di tanto in tanto riscontriamo questo problema anche sul nostro sito web. Il colpevole nel nostro caso è che le nostre statistiche / indici non sono aggiornate. Questo fa sì che una query in esecuzione precedentemente veloce diventi (eventualmente) lenta e timeout.

Prova ad aggiornare le statistiche e / o ricostruire gli indici sulle tabelle interessate dalla query e vedere se ciò aiuta.


3
Penso che ciò spiegherebbe il motivo per cui una query potrebbe scadere, ma non penso che ciò spiegherebbe perché si verifica un timeout durante il tentativo di ottenere una connessione.
DrGriff,

1
È possibile che con un indice errato le query richiedano più tempo e vengano utilizzate più connessioni contemporaneamente.
LosManos,

1
Ha funzionato per me dopo aver aggiornato tutte le statistiche con sp_updatestats su un db: EXEC sp_updatestats;
Boateng

6

È possibile specificare la dimensione minima e massima del pool specificando MinPoolSize=xyze / o MaxPoolSize=xyznella stringa di connessione. Questa causa del problema potrebbe tuttavia essere diversa.


3
Qual è la MaxPoolSize consigliata?
Amr Elgarhy,

2
Probabilmente, il modo migliore per procedere è di non specificarlo a meno che tu non abbia requisiti speciali e conosci una buona dimensione del pool per il tuo caso specifico .
Mehrdad Afshari,

1
Nota: ho controllato e ho scoperto che le connessioni attive al mio db sono circa 22 live, è troppo?
Amr Elgarhy,

Io non la penso così. La dimensione del pool predefinito è 100 connessioni credo. Dipende dal carico di ciascuna connessione sulla rete e dal server SQL. Se quelli eseguono query pesanti, potrebbe causare problemi. Inoltre, potrebbero verificarsi problemi di rete all'avvio di una nuova connessione e causare tale eccezione.
Mehrdad Afshari,

5

Ho riscontrato questo problema anche quando utilizzo un livello dati di terze parti in una delle mie applicazioni .NET. Il problema era che il layer non chiudeva correttamente le connessioni.

Abbiamo eliminato il livello e ne abbiamo creato uno da soli, che chiude e elimina sempre le connessioni. Da allora non abbiamo più l'errore.


Stiamo usando LLBL e il sito Web funziona da 2 anni e solo gli ultimi giorni hanno iniziato ad agire in questo modo.
Amr Elgarhy,

5

Puoi provare anche quello, per risolvere il problema del timeout:

Se non hai aggiunto httpRuntime al tuo webconfig, aggiungilo nel <system.web>tag

<sytem.web>
     <httpRuntime maxRequestLength="20000" executionTimeout="999999"/>
</system.web>

e

Modifica la stringa di connessione in questo modo;

 <add name="connstring" connectionString="Data Source=DSourceName;Initial Catalog=DBName;Integrated Security=True;Max Pool Size=50000;Pooling=True;" providerName="System.Data.SqlClient" />

All'ultimo utilizzo

    try
    {...} 
    catch
    {...} 
    finaly
    {
     connection.close();
    }

3

Ciò è dovuto principalmente alla connessione non chiusa nell'applicazione. Utilizzare "MinPoolSize" e "MaxPoolSize" nella stringa di connessione.


3

Nel mio caso, non stavo chiudendo l'oggetto DataReader.

        using (SqlCommand dbCmd = new SqlCommand("*StoredProcedureName*"))
        using (dbCmd.Connection = new SqlConnection(WebConfigurationAccess.ConnectionString))
            {
            dbCmd.CommandType = CommandType.StoredProcedure;

            //Add parametres
            dbCmd.Parameters.Add(new SqlParameter("@ID", SqlDbType.Int)).Value = ID;
.....
.....
            dbCmd.Connection.Open();
            var dr = dbCmd.ExecuteReader(); //created a Data reader here
            dr.Close();    //gotta close the data reader
            //dbCmd.Connection.Close(); //don't need this as 'using' statement should take care of this in its implicit dispose method.
            }

2

Se stai lavorando su un codice legacy complesso in cui un semplice utilizzo (..) {..} non è possibile - come lo ero io - potresti voler controllare lo snippet di codice che ho pubblicato in questa domanda SO per un modo per determinare il stack di chiamate della creazione della connessione quando una connessione è potenzialmente trapelata (non chiusa dopo un timeout impostato). Questo rende abbastanza facile individuare la causa delle perdite.


2

Non creare un'istanza della connessione sql troppe volte. Apri una o due connessioni e usale per tutte le successive operazioni sql.

Sembra che anche quando si Disposeeseguono le connessioni, viene generata l'eccezione.


2

Oltre alle soluzioni pubblicate ....

Nella gestione di 1000 pagine di codice legacy, ciascuna chiamata più volte a un GetRS comune, ecco un altro modo per risolvere il problema:

In una DLL comune esistente, abbiamo aggiunto l' opzione CommandBehavior.CloseConnection :

    static public IDataReader GetRS(String Sql)
    {
        SqlConnection dbconn = new SqlConnection(DB.GetDBConn());
        dbconn.Open();
        SqlCommand cmd = new SqlCommand(Sql, dbconn);
        return cmd.ExecuteReader(CommandBehavior.CloseConnection);   
    }

Quindi in ogni pagina, finché si chiude il lettore di dati, anche la connessione viene automaticamente chiusa in modo da evitare perdite di connessione.

    IDataReader rs = CommonDLL.GetRS("select * from table");
    while (rs.Read())
    {
        // do something
    }
    rs.Close();   // this also closes the connection

2

Ho appena avuto lo stesso problema e volevo condividere ciò che mi ha aiutato a trovare l'origine: aggiungere il nome dell'applicazione alla stringa di connessione e quindi controllare la connessione aperta a SQL Server

select st.text,
    es.*, 
    ec.*
from sys.dm_exec_sessions as es
    inner join sys.dm_exec_connections as ec on es.session_id = ec.session_id
    cross apply sys.dm_exec_sql_text(ec.most_recent_sql_handle) st
where es.program_name = '<your app name here>'

1

Questo problema ho riscontrato nel mio codice. Incollerò qualche codice di esempio che ho superato è venuto sotto errore. Il periodo di timeout è trascorso prima di ottenere una connessione dal pool. Ciò potrebbe essersi verificato perché tutte le connessioni in pool erano in uso e la dimensione massima del pool è stata raggiunta.

 String query = "insert into STATION2(ID,CITY,STATE,LAT_N,LONG_W) values('" + a1 + "','" + b1 + "','" + c1 + "','" + d1 + "','" + f1 + "')";
    //,'" + d1 + "','" + f1 + "','" + g1 + "'

    SqlConnection con = new SqlConnection(mycon);
    con.Open();
    SqlCommand cmd = new SqlCommand();
    cmd.CommandText = query;
    cmd.Connection = con;
    cmd.ExecuteNonQuery();
    **con.Close();**

Vuoi chiudere la connessione ogni volta. Prima di allora non ci siamo collegati da vicino a causa di questo ho ricevuto un errore. Dopo aver aggiunto la dichiarazione stretta ho superato questo errore


0

Hai perso le connessioni sul tuo codice. Puoi provare a usare usando per certificare che li stai chiudendo.

 Using (SqlConnection sqlconnection1 = new SqlConnection(“Server=.\\SQLEXPRESS ;Integrated security=sspi;connection timeout=5”)) {
                          sqlconnection1.Open();
                          SqlCommand sqlcommand1 = sqlconnection1.CreateCommand();
                          sqlcommand1.CommandText = raiserror (‘This is a fake exception’, 17,1)”;
                          sqlcommand1.ExecuteNonQuery();  //this throws a SqlException every time it is called.
                          sqlconnection1.Close(); //Still never gets called.
              } // Here sqlconnection1.Dispose is _guaranteed_

https://blogs.msdn.microsoft.com/angelsb/2004/08/25/connection-pooling-and-the-timeout-expired-exception-faq/


0

Questo problema ho riscontrato prima. Alla fine è stato un problema con il firewall. Ho appena aggiunto una regola al firewall. Ho dovuto aprire la porta in 1433modo che il server SQL potesse connettersi al server.


0

Usa questo:

finally
{
    connection.Close();
    connection.Dispose();
    SqlConnection.ClearPool();
}

12
Forse mi manca il punto SqlConnection.ClearPool, ma cosa impedisce che la tua connessione corrente venga rilasciata di nuovo al pool di connessioni? Ho pensato che l'idea del pool di connessioni fosse quella di consentire connessioni più veloci. Sicuramente rilasciando la connessione dal pool ogni volta che viene terminata, è necessario creare una NUOVA connessione ogni volta che ne è necessaria una invece di estrarne una di riserva dal pool? Spiegare come e perché questa tecnica è utile.
Dib,

0

Sì, c'è un modo per cambiare la configurazione. Se sei su un server dedicato e hai semplicemente bisogno di più connessioni SQL, puoi aggiornare le voci "dimensione massima del pool" in entrambe le stringhe di connessione seguendo queste istruzioni:

  1. Accedi al tuo server utilizzando Desktop remoto
  2. Apri Risorse del computer (Windows - E) e vai a C: \ inetpub \ vhosts [dominio] \ httpdocs
  3. Fare doppio clic sul file web.config. Questo può essere elencato come web se la struttura del file è impostata per nascondere le estensioni. Questo aprirà Visual Basic o un editor simile.
  4. Trova le stringhe di connessione, saranno simili agli esempi seguenti:

    "add name =" SiteSqlServer "connectionString =" server = (local); database = dbname; uid = dbuser; pwd = dbpassword; pooling = true; durata della connessione = 120; dimensione massima del pool = 25; ""

5. Cambiare la dimensione massima del pool = valore X nella dimensione del pool richiesta.

  1. Salvare e chiudere il file web.config.


0

Nel mio caso, avevo un ciclo infinito (da una proprietà get che cercava di ottenere valore dal database) che continuava ad aprire centinaia di connessioni SQL.

Per riprodurre il problema, provare questo:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
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.