Wie kann ich ein Verbindungspoolproblem zwischen ASP.NET und SQL Server lösen?


211

In den letzten Tagen sehen wir diese Fehlermeldung auf unserer Website zu oft:

"Zeitüberschreitung abgelaufen. Die Zeitüberschreitung vor dem Abrufen einer Verbindung aus dem Pool ist abgelaufen. Dies kann aufgetreten sein, weil alle Poolverbindungen verwendet wurden und die maximale Poolgröße erreicht wurde."

Wir haben seit einiger Zeit nichts an unserem Code geändert. Ich habe den Code überarbeitet, um offene Verbindungen zu überprüfen, die nicht geschlossen wurden, aber alles in Ordnung fand.

  • Wie kann ich das lösen?

  • Muss ich diesen Pool bearbeiten?

  • Wie kann ich die maximale Anzahl von Verbindungen dieses Pools bearbeiten?

  • Was ist der empfohlene Wert für eine stark frequentierte Website?


Aktualisieren:

Muss ich etwas in IIS bearbeiten?

Aktualisieren:

Ich habe festgestellt, dass die Anzahl der aktiven Verbindungen zwischen 15 und 31 liegt, und ich habe festgestellt, dass die maximal zulässige Anzahl der in SQL Server konfigurierten Verbindungen mehr als 3200 Verbindungen beträgt, 31 zu viele sind oder ich etwas in der ASP.NET-Konfiguration bearbeiten sollte ?


Die Standardeinstellung für die maximale Poolgröße ist 100, wenn ich mich richtig erinnere. Die meisten Websites verwenden unter hoher Last nicht mehr als 50 Verbindungen. Dies hängt davon ab, wie lange Ihre Abfragen dauern. Kurzfristige Korrektur in Verbindungszeichenfolge: Versuchen Sie, einen höheren Wert in Ihren Verbindungszeichenfolgen
festzulegen

wie viel? zum Beispiel 200 machen?
Amr Elgarhy

3
Ich denke, Sie sollten wirklich suchen, was das Problem verursacht. Laufen Ihre Abfragen (oder einige von ihnen) sehr lange?
Splattne

Vielleicht ist das der wahre Grund, eine Abfrage, deren Ausführung zu lange dauert. Ich werde darin suchen, danke
Amr Elgarhy

1
Ich hoffe, Sie können das Problem finden. Ein Vorschlag: Wenn Sie SQL Server verwenden, versuchen Sie es mit dem "SQL Profiler" und suchen Sie nach langen Abfragen: sql-server-performance.com/articles/per/…
splattne

Antworten:


218

In den meisten Fällen hängen Verbindungspooling-Probleme mit "Verbindungslecks" zusammen. Ihre Anwendung schließt ihre Datenbankverbindungen wahrscheinlich nicht korrekt und konsistent. Wenn Sie Verbindungen offen lassen, bleiben sie blockiert, bis der .NET-Garbage Collector sie für Sie schließt, indem Sie ihre Finalize()Methode aufrufen .

Sie möchten sicherstellen, dass Sie die Verbindung wirklich schließen . Der folgende Code verursacht beispielsweise ein Verbindungsleck, wenn der Code zwischen .Openund Closeeine Ausnahme auslöst:

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

Der richtige Weg wäre folgender:

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

oder

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

Wenn Ihre Funktion eine Verbindung von einer Klassenmethode zurückgibt, stellen Sie sicher, dass Sie sie lokal zwischenspeichern und ihre CloseMethode aufrufen . Mit diesem Code wird beispielsweise eine Verbindung unterbrochen:

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

Die vom ersten Anruf an zurückgegebene Verbindung getConnection()wird nicht geschlossen. Anstatt Ihre Verbindung zu schließen, erstellt diese Zeile eine neue und versucht, sie zu schließen.

Wenn Sie SqlDataReaderoder a verwenden OleDbDataReader, schließen Sie sie. Auch wenn das Schließen der Verbindung selbst den Trick zu tun scheint, sollten Sie zusätzliche Anstrengungen unternehmen, um Ihre Datenleserobjekte explizit zu schließen, wenn Sie sie verwenden.


Dieser Artikel " Warum läuft ein Verbindungspool über? " Aus dem MSDN / SQL-Magazin erläutert viele Details und schlägt einige Debugging-Strategien vor:

  • Führen Sie sp_whooder sp_who2. Diese gespeicherten Systemprozeduren geben Informationen aus der sysprocessesSystemtabelle zurück, die den Status und Informationen zu allen Arbeitsprozessen anzeigen. Im Allgemeinen wird pro Verbindung eine Serverprozess-ID (SPID) angezeigt. Wenn Sie Ihre Verbindung mit dem Argument Anwendungsname in der Verbindungszeichenfolge benannt haben, sind Ihre Arbeitsverbindungen leicht zu finden.
  • Verwenden Sie SQL Server Profiler mit der SQLProfiler- TSQL_ReplayVorlage, um offene Verbindungen zu verfolgen. Wenn Sie mit Profiler vertraut sind, ist diese Methode einfacher als das Abrufen mit sp_who.
  • Verwenden Sie den Leistungsmonitor, um die Pools und Verbindungen zu überwachen. Ich diskutiere diese Methode gleich.
  • Überwachen Sie die Leistungsindikatoren im Code. Sie können den Zustand Ihres Verbindungspools und die Anzahl der hergestellten Verbindungen überwachen, indem Sie Routinen zum Extrahieren der Zähler oder die neuen .NET PerformanceCounter-Steuerelemente verwenden.

4
Eine kleine Korrektur: Der GC ruft niemals die Dispose-Methode eines Objekts auf, sondern nur dessen Finalizer (falls vorhanden). Der Finalizer kann dann bei Bedarf einen "Fallback" -Aufruf ausführen, um zu entsorgen, obwohl ich nicht sicher bin, ob SqlConnection dies tut.
LukeH

1
Gibt es irgendeine Art von Leistungsverschlechterung, wenn wir die maximale Poolgröße 50 einstellen müssen und wir nur wenige Benutzer haben?
Mahesh Sharma

37

Bei der Installation von .NET Framework v4.6.1 begannen unsere Verbindungen zu einer entfernten Datenbank aufgrund dieser Änderung sofort eine Zeitüberschreitung .

Um dies zu beheben, fügen Sie einfach den Parameter TransparentNetworkIPResolutionin die Verbindungszeichenfolge ein und setzen Sie ihn auf false :

Server = myServerName; Database = myDataBase; Trusted_Connection = True; TransparentNetworkIPResolution = False


In diesem Fall lautet das Problem "Die Zeitüberschreitung, die verstrichen ist, bevor eine Verbindung aus dem Pool hergestellt wird". Wurde das Problem durch die Behebung Ihrer Verbindungszeichenfolge behoben, oder handelte es sich um ein separates Handshake-Problem?
FBryant87

1
Soweit ich mich erinnere, war es genau die gleiche Fehlermeldung wie in der Frage. Es trat unmittelbar nach dem Update auf .NET Framework v4.6.1 auf.
Ajbeaven

Dies war auch bei mir der Fall. Es wurde für mich auf einem App-Dienst behoben, den ich unter Azure ausführte und der eine Verbindung zu einer Azure SQL-Datenbank herstellte. Ich habe Dapper verwendet und Verbindungen korrekt entsorgt, habe jedoch immer noch die Fehlermeldung "Zeitüberschreitung vor dem Abrufen einer Verbindung aus dem Pool" erhalten. Aber nicht mehr, also danke @ajbeaven
Steve Kennaird

13

Wenn Ihre Nutzung nicht stark gestiegen ist, ist es unwahrscheinlich, dass nur ein Arbeitsstau vorliegt. IMO, die wahrscheinlichste Option ist, dass etwas Verbindungen verwendet und diese nicht sofort freigibt. Sind Sie sicher, dass Sie usingin allen Fällen verwenden? Oder (durch welchen Mechanismus auch immer) die Verbindungen lösen?


13

Haben Sie vor dem Schließen der Verbindung oder eines Datenarders nach nicht geschlossenen DataReadern und response.redirects gesucht? Verbindungen bleiben geöffnet, wenn Sie sie vor einer Umleitung nicht schließen.


3
+1 - Oder Funktionen, die DataReaders zurückgeben - Die Verbindung wird außerhalb der von Ihnen erstellten Funktion niemals geschlossen ...
splattne

1
Wenn Ihre Funktion SqlDataReader zurückgibt, ist es besser, sie in DataTable zu konvertieren, als die maximale Poolgröße zu erhöhen
live-love

10

Dieses Problem tritt von Zeit zu Zeit auch auf unserer Website auf. Der Schuldige in unserem Fall ist, dass unsere Statistiken / Indizes nicht mehr aktuell sind. Dies führt dazu, dass eine zuvor schnell laufende Abfrage (irgendwann) langsam wird und eine Zeitüberschreitung auftritt.

Versuchen Sie, Statistiken zu aktualisieren und / oder die Indizes für die von der Abfrage betroffenen Tabellen neu zu erstellen, und prüfen Sie, ob dies hilfreich ist.


3
Ich denke, dass dies erklären würde, warum eine Abfrage eine Zeitüberschreitung verursachen könnte, aber ich denke nicht, dass dies erklären würde, warum eine Zeitüberschreitung auftritt, wenn versucht wird, eine Verbindung herzustellen.
DrGriff

1
Es kann sein, dass bei einem schlechten Index die Abfragen länger dauern und gleichzeitig mehr Verbindungen verwendet werden.
LosManos

1
Arbeitete für mich nach dem Aktualisieren aller Statistiken mit sp_updatestats auf einer Datenbank: EXEC sp_updatestats;
Boateng

6

Sie können die minimale und maximale Poolgröße angeben, indem Sie MinPoolSize=xyzund / oder MaxPoolSize=xyzin der Verbindungszeichenfolge angeben . Diese Ursache des Problems könnte jedoch eine andere sein.


3
Was ist die empfohlene MaxPoolSize?
Amr Elgarhy

2
Der beste Weg ist wahrscheinlich, es nicht anzugeben, es sei denn, Sie haben spezielle Anforderungen und Sie kennen eine gute Poolgröße für Ihren speziellen Fall .
Mehrdad Afshari

1
Hinweis: Ich habe überprüft und festgestellt, dass die aktiven Verbindungen zu meiner Datenbank etwa 22 Live-Verbindungen umfassen. Ist das zu viel?
Amr Elgarhy

Das glaube ich nicht. Die Standardpoolgröße beträgt 100 Verbindungen, denke ich. Dies hängt von der Auslastung jeder Verbindung im Netzwerk und im SQL Server ab. Wenn auf diesen umfangreiche Abfragen ausgeführt werden, kann dies zu Problemen führen. Außerdem können beim Initiieren einer neuen Verbindung Netzwerkprobleme auftreten, die diese Ausnahme verursachen können.
Mehrdad Afshari

5

Dieses Problem ist auch bei der Verwendung einer Datenschicht eines Drittanbieters in einer meiner .NET-Anwendungen aufgetreten. Das Problem war, dass die Schicht die Verbindungen nicht richtig schloss.

Wir haben die Schicht weggeworfen und selbst eine erstellt, die die Verbindungen immer schließt und entsorgt. Seitdem bekommen wir den Fehler nicht mehr.


Wir verwenden LLBL und die Website läuft seit 2 Jahren und erst in den letzten Tagen hat sich das so verhalten.
Amr Elgarhy

5

Sie können dies auch versuchen, um das Timeout-Problem zu lösen:

Wenn Sie Ihrer Webkonfiguration keine httpRuntime hinzugefügt haben, fügen Sie diese im <system.web>Tag hinzu

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

und

Ändern Sie Ihre Verbindungszeichenfolge wie folgt.

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

Endlich benutzen

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

3

Dies liegt hauptsächlich daran, dass die Verbindung in der Anwendung nicht geschlossen wurde. Verwenden Sie "MinPoolSize" und "MaxPoolSize" in der Verbindungszeichenfolge.


3

In meinem Fall habe ich das DataReader-Objekt nicht geschlossen.

        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

Wenn Sie an komplexem Legacy-Code arbeiten, bei dem eine einfache Verwendung von (..) {..} nicht möglich ist - wie ich -, sollten Sie das Code-Snippet, das ich in dieser SO-Frage gepostet habe, überprüfen, um eine Möglichkeit zu finden, das zu bestimmen Aufrufstapel der Verbindungserstellung, wenn eine Verbindung möglicherweise durchgesickert ist (nach einem festgelegten Zeitlimit nicht geschlossen). Dies macht es ziemlich einfach, die Ursache der Lecks zu erkennen.


2

Instanziieren Sie die SQL-Verbindung nicht zu oft. Öffnen Sie eine oder zwei Verbindungen und verwenden Sie sie für alle nächsten SQL-Vorgänge.

Scheint, dass selbst beim DisposeHerstellen der Verbindungen die Ausnahme ausgelöst wird.


2

Neben den veröffentlichten Lösungen ....

Im Umgang mit 1000 Seiten Legacy-Code, die jeweils mehrmals ein gemeinsames GetRS aufrufen, können Sie das Problem auf folgende Weise beheben:

In einer vorhandenen gemeinsamen DLL haben wir die Option CommandBehavior.CloseConnection hinzugefügt :

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

Solange Sie den Datenleser schließen, wird auf jeder Seite die Verbindung automatisch geschlossen, um Verbindungslecks zu vermeiden.

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

2

Ich hatte gerade das gleiche Problem und wollte mitteilen, was mir beim Auffinden der Quelle geholfen hat: Fügen Sie den Anwendungsnamen zu Ihrer Verbindungszeichenfolge hinzu und überwachen Sie dann die offene Verbindung zum 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

Dieses Problem hatte ich in meinem Code. Ich werde einen Beispielcode einfügen, den ich über den Fehler gebracht habe. Die Zeitüberschreitung verstrichen, bevor eine Verbindung aus dem Pool hergestellt wurde. Dies kann aufgetreten sein, weil alle Poolverbindungen verwendet wurden und die maximale Poolgröße erreicht wurde.

 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();**

Sie möchten die Verbindung jedes Mal schließen. Vorher habe ich uns nicht die enge Verbindung gemacht, deshalb habe ich einen Fehler bekommen. Nach dem Hinzufügen von close Anweisung bin ich über diesen Fehler gekommen


0

Sie haben Verbindungen in Ihrem Code durchgesickert. Sie können versuchen, mithilfe von zu bestätigen, dass Sie sie schließen.

 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

Dieses Problem habe ich schon einmal angetroffen. Es war schließlich ein Problem mit der Firewall. Ich habe der Firewall gerade eine Regel hinzugefügt. Ich musste den Port öffnen, 1433damit der SQL Server eine Verbindung zum Server herstellen kann.


0

Benutze das:

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

12
Vielleicht fehlt mir der Punkt SqlConnection.ClearPool, aber verhindert das nur, dass Ihre aktuelle Verbindung wieder in den Verbindungspool freigegeben wird? Ich dachte, die Idee des Verbindungspools wäre, schnellere Verbindungen zu ermöglichen. Wenn Sie die Verbindung sicher jedes Mal aus dem Pool freigeben, wenn sie beendet ist, muss JEDES MAL eine NEUE Verbindung erstellt werden, anstatt eine Ersatzverbindung aus dem Pool zu ziehen? Bitte erläutern Sie, wie und warum diese Technik nützlich ist.
Dib

0

Ja, es gibt eine Möglichkeit, die Konfiguration zu ändern. Wenn Sie sich auf einem dedizierten Server befinden und einfach mehr SQL-Verbindungen benötigen, können Sie die Einträge "Maximale Poolgröße" in beiden Verbindungszeichenfolgen mithilfe der folgenden Anweisungen aktualisieren:

  1. Melden Sie sich mit Remotedesktop bei Ihrem Server an
  2. Öffnen Sie den Arbeitsplatz (Windows - E) und wechseln Sie zu C: \ inetpub \ vhosts [domain] \ httpdocs
  3. Doppelklicken Sie auf die Datei web.config. Dies kann nur als Web aufgeführt werden, wenn die Dateistruktur so eingestellt ist, dass Erweiterungen ausgeblendet werden. Dadurch wird Visual Basic oder ein ähnlicher Editor geöffnet.
  4. Suchen Sie Ihre Verbindungszeichenfolgen. Diese sehen in den folgenden Beispielen ähnlich aus:

    "add name =" SiteSqlServer "connectionString =" server = (local); database = dbname; uid = dbuser; pwd = dbpassword; pooling = true; Verbindungslebensdauer = 120; maximale Poolgröße = 25; ""

5.Ändern Sie den Wert für die maximale Poolgröße = X an die erforderliche Poolgröße.

  1. Speichern und schließen Sie Ihre Datei web.config.


0

In meinem Fall hatte ich eine Endlosschleife (von einer get-Eigenschaft, die versucht, einen Wert aus der Datenbank abzurufen), die immer wieder Hunderte von SQL-Verbindungen öffnete.

Um das Problem zu reproduzieren, versuchen Sie Folgendes:

while (true)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        someCall(connection);
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.