Buforowanie połączeń SQL Server przy użyciu Microsoft.Data.SqlClient

Mechanizm puli połączeń w Microsoft.Data.SqlClient ponownie wykorzystuje uwierzytelnione połączenia fizyczne. SqlConnection.Open lub OpenAsync sprawdza pulę pod kątem dostępnego połączenia. Close, Dispose lub DisposeAsync resetują i przywracają ją. Takie podejście unika połączenia sieciowego, uwierzytelniania i konfiguracji sesji przy każdej operacji.

Łączenie jest domyślnie włączone. Użyj tego wzorca aplikacji:

await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);

using var command = new SqlCommand(sql, connection);
await command.ExecuteNonQueryAsync(cancellationToken);

Otwieraj jak najpóźniej, zwalniaj jak najwcześniej i pozwól puli zarządzać fizycznymi połączeniami. Nie pozostawiaj SqlConnection otwartego globalnie.

Poznaj klucze puli

Połączenie może być użyte ponownie wyłącznie z odpowiadającej mu puli. Klucz pulowy obejmuje więcej niż tylko serwer docelowy.

Input Zachowanie puli
Parametry połączenia Tekst musi się dokładnie zgadzać. Różnice w kolejności słów kluczowych tworzą oddzielne pule, nawet gdy efektywne ustawienia są równe.
Zintegrowane uwierzytelnianie systemu Windows Tożsamość Windows jest częścią klucza. Ten sam ciąg danych pod różnymi tożsamościami tworzy różne pule.
SqlCredential Instancja obiektu jest częścią klucza. Oddzielne instancje tworzą oddzielne pule, nawet jeśli zawierają tę samą nazwę użytkownika i hasło.
SqlConnection.AccessToken Wartość tokena dostępu jest częścią klucza. Zastąpienie ciągów tokenów może spowodować utworzenie nowych pul i pozostawić w istniejących pulach połączenia uwierzytelnione za pomocą starych tokenów.
SqlConnection.AccessTokenCallback Funkcja zwrotna jest częścią klucza. Użyj ponownie tej samej instancji funkcji wywołania zwrotnego dla połączeń, które powinny korzystać ze wspólnej puli. Wartość zwróconego tokena nie jest kluczem puli.
Niestandardowy dostawca kontekstu SSPI Instancja dostawcy uczestniczy w konfiguracji połączenia. Wykorzystaj jedną instancję dostawcy do połączeń, które powinny się łączyć.
Transakcja kontekstowa Przyłączone połączenia korzystają z podziałów właściwych dla danej transakcji w puli dopasowującej.

Baza danych, tryb uwierzytelniania, opcje szyfrowania, nazwa aplikacji, ustawienia puli połączeń oraz każda inna wartość parametrów połączenia współtworzą ten dokładny ciąg połączenia.

Zbuduj jeden kanoniczny ciąg połączenia i używaj go ponownie. Unikaj wartości specyficznych dla żądania w Application Name, Workstation ID lub innych słowach kluczowych.

Wybierz API tokenów, które mogą się poolować

W przypadku tokenów dostępu Microsoft Entra ID użyj trybu uwierzytelniania udostępnianego przez Microsoft.Data.SqlClient lub stabilnego AccessTokenCallback.

AccessTokenCallback został wprowadzony w Microsoft.Data.SqlClient 5.2. Sterownik wywołuje ją, gdy potrzebuje tokenu, i może zażądać odświeżonego tokenu dla ponownie używanej puli. Zachowaj deterministyczne działanie funkcji wywołania zwrotnego dla parametrów uwierzytelniania przekazywanych przez sterownik i używaj ponownie tej samej instancji delegata.

Gdy kod ustawia AccessToken bezpośrednio:

  • Ciąg tokenów staje się częścią klucza puli.
  • Aplikacja odpowiada za wygasanie i odświeżanie tokena.
  • Fizyczne połączenie z puli może istnieć dłużej niż token użyty do jego utworzenia.
  • Zadzwoń ClearPool po wymianie wygasłego tokena, jeśli ta pula nie może być już bezpiecznie używana.

Nie tworz nowego callback lambda ani obiektu uwierzytelniania dla każdego żądania. Różnice w tożsamości obiektów mogą prowadzić do fragmentacji pul.

Microsoft.Data.SqlClient 7.0 dodaje SspiContextProvider do niestandardowej negocjacji protokołów Kerberos lub NTLM. Traktuj dostawcę jako konfigurację połączenia dostosowaną do zakresu aplikacji, a nie stan na każde żądanie.

Rozmiar każdego basenu

Te opcje parametrów połączenia sterują jedną pulą:

Keyword Default Efekt
Pooling true Włącza lub wyłącza pooling.
Min Pool Size 0 Ustala minimalną liczbę fizycznych połączeń, które pula zachowuje po jej utworzeniu.
Max Pool Size 100 Ustala maksymalną liczbę fizycznych połączeń w puli.
Connect Timeout 15 sekund Ustala, jak długo Open czeka, gdy nie ma dostępnego użytecznego połączenia.
Load Balance Timeout 0 Sekund Odrzuca połączenie, gdy wraca do puli, jeśli jego wiek przekracza skonfigurowaną wartość. Connection Lifetime jest aliasem.

Pula tworzy połączenia wraz ze wzrostem zapotrzebowania, aż do osiągnięcia Max Pool Size. Gdy wszystkie połączenia są już używane, późniejsze otwarcia czekają na powrót połączenia. Jeśli czas oczekiwania przekracza Connect Timeout, otwarcie kończy się niepowodzeniem.

Nie podnoś Max Pool Size przed sprawdzeniem:

  • Każde połączenie i każdy odczyt są umieszczone na każdej ścieżce.
  • Komendy i transakcje są realizowane szybko.
  • Obciążenie zapytaniami nie jest blokowane ani przesycone.
  • Limit połączeń do bazy danych może obsłużyć wartość Max Pool Size pomnożoną przez każdą pulę w każdej instancji aplikacji.

Dodatnia wartość parametru Min Pool Size utrzymuje połączenia otwarte w okresach bezczynności. Używaj go tylko wtedy, gdy pomiary uzasadniają ciepłe połączenia. Zwykle nie sprawdza się w architekturach typu scale-to-zero, bezserwerowym automatycznym wstrzymywaniu oraz architekturach chmurowych typu burstable.

Domyślnie, przy ustawieniu Load Balance Timeout=0, okresowe czyszczenie zwykle usuwa nieużywane połączenia przekraczające wartość Min Pool Size po około 4–8 minutach albo pula usuwa je po wykryciu, że połączenie z serwerem zostało zerwane. Traktuj ten interwał jako zachowanie implementacyjne, a nie gwarancję bezczynności dla każdego połączenia. Pula połączeń nie wysyła zapytania walidacyjnego przed każdym pobraniem połączenia, ponieważ taka komunikacja tam i z powrotem niweluje znaczną część korzyści płynących z używania puli połączeń.

Obsługa okresów blokowania uwierzytelniania

Po przekroczeniu limitu czasu uwierzytelniania lub innym niepowodzeniu uwierzytelniania pula może przejść w stan blokady. W tym czasie próby otwarcia spełniające te same warunki ponownie zgłaszają oryginalny wyjątek bez podejmowania kolejnej próby uwierzytelnienia.

Pierwszy okres blokowania trwa pięć sekund. Po kolejnej porażce okres podwaja się do jednej minuty.

Pool Blocking Period kontroluje to zachowanie:

Wartość Behavior
Auto Włącza blokowanie zwykłych punktów końcowych SQL Server i wyłącza je dla rozpoznanych sufiksów punktów końcowych Azure SQL. Niestandardowa nazwa DNS może nie wykazywać działania platformy Azure.
AlwaysBlock Włącza okres blokowania dla każdego punktu końcowego.
NeverBlock Wyłącza okres blokowania.

Zachowaj Auto, chyba że zmierzona strategia ponawiania prób aplikacji wymaga innego wyboru. Wyłączenie okresu blokowania może zamienić problem z poświadczeniami, zaporą sieciową lub awarią w burzę uwierzytelniania.

Okres blokowania jest oddzielny od konfigurowalnej logiki powtórek. Dostawca powtórek, który otwiera tę samą pulę podczas okresu blokowania, otrzymuje wyjątek w pamięci podręcznej.

Zarządzaj żywotnością połączenia i czyszczeniem połączenia

Pula automatycznie usuwa dotkniętą pulę, gdy rozpozna błąd fatalny, taki jak przełączanie awaryjne. Pula zamyka połączenia bezczynne i odrzuca wyczekane połączenia, gdy wracają.

Użyj API clearing dla znanej granicy konfiguracji lub poświadczenia:

  • ClearPool oczyszcza pulę powiązaną z jedną SqlConnection konfiguracją.
  • ClearAllPools czyści wszystkie pule Microsoft.Data.SqlClient w procesie lub domenie aplikacji.

Basen zamyka połączenia bezczynne w oczyszczonym basenie. Pula oznacza połączenia aktualnie używane, więc po zwróceniu je odrzuca.

Wyczyszczenie pul powoduje, że kolejne otwarcia połączeń wymagają fizycznego logowania. Nie używaj go jako okresowej konserwacji, ogólnego narzędzia do obsługi błędów ani jako zamiennika do usuwania połączeń.

Load Balance Timeout zapewnia stopniową rotację w zależności od wieku. Używaj go, gdy wdrożenie lub usługa klastrowana wymaga opuszczenia starych fizycznych połączeń z czasem. Sprawdź, czy wybrana wartość nie powoduje nadmiernej liczby wymuszonych połączeń.

Poznaj transakcje

W przypadku System.Transactions.Transaction.Current, ustawienia domyślnego, połączenie otwarte wewnątrz Enlist=true automatycznie dołącza do tej transakcji.

Gdy połączenie przyłączone do transakcji zostaje zamknięte, pula umieszcza je w podpuli specyficznej dla danej transakcji. Późniejsze otwarcie w ramach tej samej transakcji może ponownie go wykorzystać. Fizyczne połączenie nie wraca do puli ogólnej, dopóki transakcja się nie zakończy.

Długie lub porzucone transakcje ambientowe mogą więc:

  • Trzymaj fizyczne kontakty z dala od ogólnej puli.
  • Zużyj pojemność puli po zamknięciu połączenia logicznego.
  • Zachowaj blokady na serwerze i stan transakcji jako aktywne.

Utrzymuj transakcje w określonych granicach, jawnie je kończ i monitoruj połączenia w stanie zastoju. Ustaw Enlist=false tylko wtedy, gdy połączenie musi pozostać poza transakcją niejawną.

Zapobieganie fragmentacji puli

Fragmentacja pul powoduje powstawanie wielu małych pul zamiast kilku pul, które można ponownie wykorzystać. Typowe przyczyny:

  • Różnice w łańcuchu połączeń, kolejności słów kluczowych lub aliasach.
  • Jeden parametry połączenia na klienta, użytkownika, żądanie lub bazę danych.
  • Zintegrowane uwierzytelnianie pod wieloma tożsamościami Windows.
  • Nowe SqlCredential, funkcja wywołania zwrotnego tokenu dostępu lub instancje dostawcy SSPI dla każdego żądania.
  • Tokeny bezpośredniego dostępu, które zmieniają się przy każdym odświeżeniu.
  • Nazwy aplikacji o wysokiej krotności lub identyfikatory stacji roboczych.

Ujednolicić parametry połączenia za pomocą SqlConnectionStringBuilder i scentralizować tworzenie połączeń.

Jeśli aplikacja celowo łączy się z wieloma bazami danych lub tożsamościami, uwzględnij powstałą liczbę pul w planowaniu przepustowości. Nie uruchamiaj USE z niezaufaną nazwą bazy danych, aby scalić pule. Izolacja bazy danych, uprawnienia, stan sesji oraz sposób resetowania puli muszą pozostać jawne.

Uwzględnienie ról aplikacji i stanu sesji

Pula połączeń resetuje stan sesji SQL Server wielokrotnego użytku przed przypisaniem fizycznego połączenia do innego połączenia logicznego. Kod aplikacji powinien nadal ustawić wymagany stan sesji w ramach jednostki pracy.

Role aplikacji SQL Server aktywowane za pomocą sp_setapprole nie mogą być bezpiecznie zresetowane w zwykłym mechanizmie puli połączeń. Preferuj użytkowników baz danych, użytkowników zamkniętych, role, bezpieczeństwo na poziomie wiersza lub inny projekt autoryzacji. Jeśli rola aplikacyjna jest nieunikniona, użyj udokumentowanego mechanizmu przywracania opartego na plikach cookie lub wyłącz pulowanie dla tej odizolowanej ścieżki po przetestowaniu.

Pozbądź się czytników, dokończ lub cofnij transakcje i nie pozostawiaj poleceń aktywnych po zamknięciu połączenia. Nie polegaj na tymczasowych tabelach ani innym stanie sesji, które przetrwają przez połączenia logiczne.

Korzystaj z wzorców poolingu hostowanego w chmurze

W przypadku usługi Azure App Service, usługi Azure Functions, kontenerów, rozwiązania Kubernetes i innych hostów skalowanych horyzontalnie:

  • Oblicz możliwe połączenia z bazą danych we wszystkich instancjach, procesach, kluczach puli oraz replikach.
  • Użyj tożsamości zarządzanej lub stabilnej funkcji wywołania zwrotnego tokenu dostępu zamiast zmieniania ciągów tokenów w obiektach połączenia.
  • Zachowaj Min Pool Size=0, chyba że potwierdzony pomiarem wymóg dotyczący zimnego startu uzasadnia utrzymywanie sesji.
  • Spodziewaj się, że nowa instancja zacznie się od pustej puli.
  • Utrzymuj identyczne ciągi połączeń w instancjach obsługujących to samo obciążenie.
  • Ogranicz liczbę prób połączenia i ponawiania prób, aby uniknąć zsynchronizowanych nagłych skoków liczby logowań podczas przełączenia awaryjnego lub skalowania wszerz.
  • Ustaw MultiSubnetFailover=true dla usługi Azure SQL i innych obsługiwanych punktów końcowych TCP z wieloma adresami.

Pule połączeń są lokalne dla procesu aplikacyjnego. Nie są współdzielone między instancjami aplikacji, kontenerami czy hostami.

Diagnozowanie działania puli

Użyj liczników diagnostycznych SqlClient , aby obserwować:

  • Bezpośrednie połączenia i rozłączenia, które oznaczają fizyczne połączenia serwerowe.
  • Miękkie łączenia i rozłączania, które reprezentują wypłatę i powrót puli.
  • Aktywne i darmowe połączenia w grupie.
  • Aktywne grupy basenowe i baseny.
  • Połączenia Stasis.
  • Odzyskane połączenia tam, gdzie kod aplikacji nie usuwał logicznego połączenia.

Koreluj liczniki klientów z sesjami, oczekiwaniami, blokowaniem i limitami zasobów SQL Server. Przekroczenie limitu czasu puli połączeń może oznaczać wyciek połączeń, wolne zapytania, zablokowane transakcje, zbyt dużą współbieżność, fragmentację puli lub limit wydajności bazy danych.

Używaj śledzenia źródła zdarzeń do docelowych śladów poolera. Śledzenie jest rozwlekłe. Włącz to dla ograniczonego okna diagnostycznego i chroń wszelkie przechwycone metadane połączenia.

Lista kontrolna produkcji

  • Utrzymuj włączone poolowanie.
  • Używaj ponownie jednych kanonicznych parametrów połączenia dla każdego obciążenia roboczego i każdej bazy danych.
  • Pozbądź się połączeń, poleceń, czytników i transakcji na każdej ścieżce.
  • Ponownie użyj instancji dostawców danych uwierzytelniających, tokenów callback oraz SSPI.
  • Ustaw skończone limity czasowe połączenia i poleceń.
  • Ustal, że całkowity budżet połączenia obejmuje każdą instancję aplikacji.
  • Monitoruj twarde połączenia, liczbę pul, wolne połączenia, stazy i timeouty.
  • Czyść pule połączeń tylko dla poświadczenia, tokenu lub granicy konfiguracji, których dostawca nie może wykryć, albo gdy diagnostyka potwierdza, że nadal pozostają przestarzałe połączenia.
  • Przed wdrożeniem produkcyjnym przetestuj obciążeniowo skalowanie w poziomie, przełączanie awaryjne i działanie odświeżania poświadczeń.