Udostępnij za pośrednictwem


Buforowanie połączeń z programem SQL Server (ADO.NET)

Nawiązywanie połączenia z serwerem bazy danych zwykle składa się z kilku czasochłonnych kroków. Należy ustanowić kanał fizyczny, taki jak gniazdo lub nazwany potok, należy przeprowadzić początkowe uzgadnianie z serwerem, należy przeanalizować informacje zawarte w parametrach połączenia, połączenie musi zostać uwierzytelnione przez serwer, należy uruchomić kontrolę w celu zarejestrowania się w bieżącej transakcji itd.

W praktyce większość aplikacji używa tylko jednej lub kilku różnych konfiguracji dla połączeń. Oznacza to, że podczas wykonywania aplikacji wiele identycznych połączeń będzie wielokrotnie otwieranych i zamykanych. Aby zminimalizować koszty otwierania połączeń, ADO.NET używa techniki optymalizacji nazywanej buforowaniem połączeń.

Uwaga

Buforowanie połączeń w ADO.NET działa spójnie we wszystkich środowiskach opartych na programie SQL Server, w tym w usługach Azure SQL Database, Azure SQL Managed Instance i SQL Server (lokalnie lub na maszynach wirtualnych). Mechanizm współdzielenia zasobów jest całkowicie po stronie klienta i funkcjonuje identycznie na tych platformach. Jednak czynniki specyficzne dla usługi mogą mieć wpływ na wydajność puli: usługa Azure SQL Database wymusza limity połączeń na podstawie wybranej warstwy usługi (np. Podstawowa, Standardowa, Premium), podczas gdy usługa Azure SQL Managed Instance łączy limity połączeń z przydzielonymi zasobami maszyny wirtualnej, takimi jak rdzenie wirtualne i pamięć. Z kolei program SQL Server na maszynach wirtualnych nie ma wymuszonych limitów wykraczanych poza ograniczenia sprzętu i licencjonowania, co zapewnia największą elastyczność.

Buforowanie połączeń zmniejsza częstotliwość otwierania nowych połączeń. Program pooler utrzymuje własność połączenia fizycznego. Zarządza połączeniami, utrzymując aktywny zestaw aktywnych połączeń dla każdej konfiguracji połączenia. Za każdym razem, kiedy użytkownik wywołuje Open na połączeniu, pooler szuka dostępnego połączenia w puli. Jeśli połączenie w puli jest dostępne, jest zwracane wywołującemu zamiast otwierania nowego połączenia. Gdy aplikacja wywołuje Close połączenie, program pooler zwraca go do zestawu aktywnych połączeń w puli, a nie zamyka go. Gdy połączenie zostanie zwrócone do puli, będzie gotowe do ponownego użycia przy następnym wywołaniu Open.

Można pulować tylko połączenia z tą samą konfiguracją. ADO.NET jednocześnie przechowuje kilka pul, po jednym dla każdej konfiguracji. Połączenia są rozdzielane w pule przez łańcuch połączenia i przez tożsamość systemu Windows w przypadku użycia zintegrowanych zabezpieczeń. Połączenia są również grupowane w oparciu o to, czy są uwzględnione w transakcji. W przypadku korzystania z ChangePassword wystąpienie SqlCredential wpływa na pulę połączeń. Różne wystąpienia SqlCredential będą używać różnych pul połączeń, nawet jeśli identyfikator użytkownika i hasło są takie same.

Użycie puli połączeń może znacznie zwiększyć wydajność i skalowalność aplikacji. Domyślnie buforowanie połączeń jest włączone w ADO.NET. Jeśli nie wyłączysz go jawnie, program pooler optymalizuje połączenia w miarę ich otwierania i zamykania w aplikacji. Można również podać kilka modyfikatorów parametrów połączenia w celu kontrolowania zachowania puli połączeń. Aby dowiedzieć się więcej, zobacz "Kontrolowanie puli połączeń za pomocą słów kluczowych ciągów połączeń" w dalszej części tego tematu.

Uwaga

Po włączeniu puli połączeń i wystąpieniu błędu przekroczenia limitu czasu lub innego błędu logowania zostanie zgłoszony wyjątek, a kolejne próby nawiązania połączenia nie powiodą się przez następne pięć sekund, czyli w okresie blokady. Jeśli aplikacja spróbuje nawiązać połączenie w okresie blokowania, pierwszy wyjątek zostanie zgłoszony ponownie. Kolejne błędy po zakończeniu okresu blokowania spowodują powstanie nowych okresów blokowania, które są dwa razy większe niż poprzedni okres blokowania, maksymalnie minutę.

Tworzenie i przypisywanie puli

Po pierwszym otwarciu połączenia, pula połączeń jest tworzona na podstawie dokładnego algorytmu dopasowania, który kojarzy pulę z ciągiem połączenia w połączeniu. Każda pula połączeń jest skojarzona z odrębnym ciągiem połączenia. Po otwarciu nowego połączenia, jeśli parametry połączenia nie są dokładnie zgodne z istniejącymi pulami, zostanie utworzona nowa pula. Połączenia są w puli na proces, na domenę aplikacji, na parametry połączenia i gdy są używane zintegrowane zabezpieczenia, na tożsamość systemu Windows. Parametry połączenia muszą być również dokładnie zgodne; słowa kluczowe podane w innej kolejności dla tego samego połączenia będą pulowane oddzielnie.

W poniższym przykładzie w języku C# są tworzone trzy nowe SqlConnection obiekty, ale do zarządzania nimi wymagane są tylko dwie pule połączeń. Należy pamiętać, że pierwsze i drugie parametry połączenia różnią się od wartości przypisanej dla elementu Initial Catalog.

using (SqlConnection connection = new SqlConnection(
  "Integrated Security=SSPI;Initial Catalog=Northwind"))
    {
        connection.Open();
        // Pool A is created.
    }

using (SqlConnection connection = new SqlConnection(
  "Integrated Security=SSPI;Initial Catalog=pubs"))
    {
        connection.Open();
        // Pool B is created because the connection strings differ.
    }

using (SqlConnection connection = new SqlConnection(
  "Integrated Security=SSPI;Initial Catalog=Northwind"))
    {
        connection.Open();
        // The connection string matches pool A.
    }

Ważne

Firma Microsoft zaleca korzystanie z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Jeśli łączysz się z usługą Azure SQL, tożsamości zarządzane dla zasobów platformy Azure to zalecana metoda uwierzytelniania.

Jeśli parametr Min Pool Size nie zostanie określony w łańcuchu połączenia lub zostanie określony do zera, połączenia w puli zostaną zamknięte po okresie nieaktywności. Jeśli jednak określona wartość Min Pool Size jest większa niż zero, pula połączeń nie zostanie zniszczona, dopóki AppDomain nie zostanie zwolniony i proces się nie zakończy. Konserwacja nieaktywnych lub pustych pul obejmuje minimalne obciążenie systemu.

Uwaga

Pula jest automatycznie czyszczona po wystąpieniu błędu krytycznego, takiego jak failover.

Dodawanie połączeń

Pula połączeń jest tworzona dla każdego unikatowego ciągu połączenia. Po utworzeniu puli tworzone i dodawane są do niej liczne obiekty połączenia, aby spełnić minimalne wymagania dotyczące rozmiaru puli. Połączenia są dodawane do puli w miarę potrzeb, aż do osiągnięcia maksymalnego rozmiaru puli, który określono na 100 (wartość domyślna). Połączenia są zwalniane z powrotem do puli po ich zamknięciu lub usunięciu.

SqlConnection Gdy obiekt jest zażądany, jest uzyskiwany z puli, jeśli dostępne jest połączenie możliwe do użycia. Aby można było używać, połączenie musi być nieużywane, mieć zgodny kontekst transakcji lub nie być skojarzone z dowolnym kontekstem transakcji i mieć prawidłowy link do serwera.

Program puli połączeń spełnia żądania dotyczące połączeń przez ponowne przydzielanie połączeń w miarę ich zwalniania z powrotem do puli. Jeśli został osiągnięty maksymalny rozmiar puli i nie jest dostępne żadne połączenie do użycia, żądanie jest kolejkowane. Następnie program puli próbuje odzyskać wszystkie połączenia do momentu osiągnięcia limitu czasu (wartość domyślna to 15 sekund). Jeśli program puli nie może spełnić żądania przed upływem limitu czasu połączenia, zostanie zgłoszony wyjątek.

Uwaga

Zdecydowanie zalecamy, aby zawsze zamykać połączenie po zakończeniu korzystania z niego, aby połączenie zostało zwrócone do puli. Można to zrobić przy użyciu metod Close lub Dispose obiektu Connection lub otwierając wszystkie połączenia wewnątrz instrukcji using w języku C# lub instrukcji Using w języku Visual Basic. Połączenia, które nie są jawnie zamknięte, mogą nie zostać dodane lub zwrócone do puli. Aby uzyskać więcej informacji, zobacz using statement lub Jak usunąć zasób systemowy w języku Visual Basic.

Uwaga

Nie należy wywoływać Close ani Dispose na Connection, DataReader ani żadnym innym obiekcie zarządzanym w metodzie Finalize swojej klasy. W finalizatorze zwalniaj tylko niezarządzane zasoby, które należą do klasy bezpośrednio. Jeśli klasa nie jest właścicielem żadnych zasobów niezarządzanych, nie dołączaj Finalize metody do definicji klasy. Aby uzyskać więcej informacji, zobacz Zbieranie śmieci.

Aby uzyskać więcej informacji na temat zdarzeń skojarzonych z otwieraniem i zamykaniem połączeniami, zobacz Audit Login Event Class i Audit Logout Event Class w dokumentacji programu SQL Server.

Usuwanie połączeń

Moduł puli połączeń usuwa połączenie z puli po bezczynności przez około 4–8 minut lub jeśli moduł puli wykryje, że połączenie z serwerem zostało zerwane. Należy pamiętać, że połączenie zerwane można wykryć dopiero po próbie nawiązania komunikacji z serwerem. Jeśli zostanie znalezione połączenie, które nie jest już połączone z serwerem, jest oznaczone jako nieprawidłowe. Nieprawidłowe połączenia są usuwane z puli połączeń tylko wtedy, gdy są zamknięte lub odzyskane.

Jeśli istnieje połączenie z serwerem, który zniknął, to połączenie można pobrać z puli, nawet jeśli moduł puli połączeń nie wykrył zerwanego połączenia i oznaczył go jako nieprawidłowe. Dzieje się tak, ponieważ obciążenie związane ze sprawdzaniem, czy połączenie jest nadal prawidłowe, wyeliminowałoby korzyści wynikające z posiadania menedżera puli przez spowodowanie wystąpienia kolejnego zapytania do serwera. W takim przypadku pierwsza próba użycia połączenia wykryje, że połączenie zostało zerwane i zostanie zgłoszony wyjątek.

Wyczyść pulę

W ADO.NET 2.0 wprowadzono dwie nowe metody czyszczenia puli: ClearAllPools i ClearPool. ClearAllPools czyści pule połączeń dla danego dostawcy i ClearPool czyści pulę połączeń skojarzona z określonym połączeniem. Jeśli w momencie wywołania są używane połączenia, są one odpowiednio oznaczone. Gdy zostaną zamknięte, zostaną one odrzucone, a nie zostaną zwrócone do puli.

Obsługa transakcji

Połączenia są pobierane z puli i przypisywane na podstawie kontekstu transakcji. O ile Enlist=false nie zostanie określony w parametrach połączenia, pula połączeń upewnia się, że połączenie jest uwzględnione w Current kontekście. Gdy połączenie zostanie zamknięte i zwrócone do puli z transakcją enlisted System.Transactions , zostanie ono odłożone w taki sposób, że następne żądanie dla tej puli połączeń z tą samą transakcją zwróci to samo System.Transactions połączenie, jeśli jest dostępne. Jeśli takie żądanie jest wystawiane i nie ma dostępnych połączeń w puli, połączenie jest pobierane z części puli nieobjętej transakcjami i dodane. Jeśli w żadnym z obszarów puli nie są dostępne żadne połączenia, zostanie utworzone i zarejestrowane nowe połączenie.

Po zamknięciu połączenia zostanie ono zwolnione z powrotem do puli i do odpowiedniej poddziału na podstawie kontekstu transakcji. W związku z tym można zamknąć połączenie bez generowania błędu, nawet jeśli transakcja rozproszona wciąż trwa. Później można zatwierdzić lub przerwać transakcję w systemie rozproszonym dzięki temu.

Kontroluj buforowanie połączeń za pomocą słów kluczowych w parametrze połączenia.

Właściwość ConnectionStringSqlConnection obiektu obsługuje pary parametry połączenia klucz/wartość, których można użyć do dostosowania zachowania logiki buforowania połączeń. Aby uzyskać więcej informacji, zobacz ConnectionString.

Fragmentacja puli

Fragmentacja puli jest typowym problemem w wielu aplikacjach internetowych, w których aplikacja może utworzyć dużą liczbę pul, które nie zostaną zwolnione do momentu zakończenia procesu. Pozostawia to dużą liczbę połączeń otwartych i zużywających pamięć, co skutkuje niską wydajnością.

Fragmentacja puli ze względu na zintegrowane zabezpieczenia

Połączenia są buforowane zgodnie z ciągiem połączenia oraz tożsamością użytkownika. W związku z tym, jeśli używasz uwierzytelniania podstawowego lub uwierzytelniania systemu Windows w witrynie sieci Web i zintegrowanego logowania zabezpieczeń, otrzymasz jedną pulę na użytkownika. Chociaż poprawia to wydajność kolejnych żądań bazy danych dla jednego użytkownika, ten użytkownik nie może korzystać z połączeń wykonanych przez innych użytkowników. Powoduje to również co najmniej jedno połączenie na użytkownika z serwerem bazy danych. Jest to efekt uboczny konkretnej architektury aplikacji internetowej, który deweloperzy muszą rozważyć w kontekście wymagań dotyczących zabezpieczeń i audytowania.

Fragmentacja puli ze względu na wiele baz danych

Wielu dostawców usług internetowych hostuje kilka witryn sieci Web na jednym serwerze. Mogą używać pojedynczej bazy danych do potwierdzenia logowania do uwierzytelniania formularzy, a następnie otworzyć połączenie z określoną bazą danych dla tego użytkownika lub grupy użytkowników. Połączenie z bazą danych uwierzytelniania jest współdzielone i używane przez wszystkich. Istnieje jednak oddzielna pula połączeń z każdą bazą danych, co zwiększa liczbę połączeń z serwerem.

Jest to również efekt uboczny projektu aplikacji. Istnieje stosunkowo prosty sposób uniknięcia tego efektu ubocznego bez naruszania zabezpieczeń podczas nawiązywania połączenia z programem SQL Server. Zamiast łączyć się z oddzielną bazą danych dla każdego użytkownika lub grupy, połącz się z tą samą bazą danych na serwerze, a następnie wykonaj instrukcję Transact-SQL USE, aby zmienić żądaną bazę danych. Poniższy fragment kodu przedstawia utworzenie początkowego połączenia z master bazą danych, a następnie przełączenie do żądanej bazy danych określonej w zmiennej ciągowej databaseName.

' Assumes that command is a SqlCommand object.
command.Text = "USE DatabaseName"
Using connection As New SqlConnection(connectionString)
    connection.Open()
    command.ExecuteNonQuery()
End Using
// Assumes that command is a SqlCommand object.
command.Text = "USE DatabaseName";
using (SqlConnection connection = new SqlConnection(
  connectionString))
  {
    connection.Open();
    command.ExecuteNonQuery();
  }

Role aplikacji i pulowanie połączeń

Po aktywowaniu roli aplikacji programu SQL Server przez wywołanie sp_setapprole procedury składowanej systemu nie można zresetować kontekstu zabezpieczeń tego połączenia. Jeśli jednak pula jest włączona, połączenie wraca do puli, a błąd występuje, gdy ponownie używane jest połączenie z puli.

Zobacz też