„Otwórz / zamknij” SqlConnection czy pozostaw otwarte?

122

Mam swoją logikę biznesową zaimplementowaną w prostych klasach statycznych z metodami statycznymi. Każda z tych metod otwiera / zamyka połączenie SQL po wywołaniu:

public static void DoSomething(string something)
{
    using (SqlConnection connection = new SqlConnection("..."))
    {
        connection.Open();

        // ...

        connection.Close();
    }
}

Myślę jednak, że unikanie otwierania i zamykania połączenia oszczędza wydajność . Kilka testów wykonałem już dawno temu z klasą OleDbConnection (nie jestem pewien co do SqlConnection) i zdecydowanie pomogło to tak działać (o ile pamiętam):

//pass the connection object into the method
public static void DoSomething(string something, SqlConnection connection)
{
    bool openConn = (connection.State == ConnectionState.Open);
    if (!openConn)
    {
        connection.Open();
    }

    // ....

    if (openConn) 
    {
        connection.Close();
    }
}

Pytanie brzmi - czy powinienem wybrać metodę (a) czy metodę (b)? Czytałem na innym pytaniu związanym z przepełnieniem stosu, że buforowanie połączeń zaoszczędziło mi wydajności, nie muszę się wcale przejmować ...

PS. Jest to aplikacja ASP.NET - połączenia istnieją tylko podczas żądania internetowego. Nie jest to aplikacja ani usługa korzystna.

Alex
źródło
1
Porada: użyj DbConnection.StateChangezdarzenia, aby monitorować zmiany stanu połączenia (i może być przechowywane lokalnie), zamiast DbConnection.Statebezpośrednio sprawdzać właściwość. Pozwoli to zaoszczędzić koszty wydajności.
decyklon
1
Brakuje tylko jednego szczegółu, w jaki sposób ta metoda jest częścią żądania strony. Czy jest to jedyna wywołana metoda, czy jest, jak założyłem w mojej odpowiedzi, jedną z wielu metod, która jest wywoływana w żądaniu strony, wpływa na to, która odpowiedź jest poprawna;)
David Mårtensson
David - Wiele takich metod jest nazywanych :)
Alex
1
Przypadek A pokazuje brak wiary w Dispose: patrz stackoverflow.com/questions/1195829/ ... i przykład na MSDN msdn.microsoft.com/en-us/library/ ...
user2864740

Odpowiedzi:

82

Trzymaj się opcji a .

Pula połączeń to Twój przyjaciel.

Adriaan Stander
źródło
37
IMHO - on nawet nie powinien się zbliżać. utylizacja to zrobi.
Royi Namir
2
@RoyiNamir Podoba mi się wezwanie do zamknięcia połączenia. Szczególnie dla początkujących i nowicjuszy w kodzie. Jest bardziej wyraźny i czytelny.
krawędzie
27
@edhedges Wykorzystanie zarówno funkcji „using”, jak i Close () ostatecznie wprowadzi zamieszanie jedynie wśród nowicjuszy. Nie zrozumieją celu używania „używania”. Nie używaj „Zamknij”, zamiast tego naucz ich celu „używania”. Aby mogli się uczyć, stawać się lepszymi i zastosować to, czego się nauczyli, do innych części kodu.
Luis Perez
1
Czy należy / czy należy wywołać funkcję „Open ()”? Obecnie używam go w ten sposób: using (var conn = GetConnection ()) {} public SqlConnection GetConnection () {return new SqlConnection (_connectionString); }
ganders
79

Za każdym razem użyj metody (a). Kiedy zaczniesz skalować swoją aplikację, logika zajmująca się stanem stanie się prawdziwym problemem, jeśli tego nie zrobisz.

Pula połączeń działa zgodnie z opisem w puszce. Pomyśl tylko, co się dzieje, gdy aplikacja się skaluje i jak trudno byłoby ręcznie zarządzać stanem otwarcia / zamknięcia połączenia. Pula połączeń świetnie sobie z tym radzi. Jeśli martwisz się o wydajność, pomyśl o jakimś mechanizmie pamięci podręcznej, aby nic nie zostało zablokowane.

WeNeedAnswers
źródło
33

Zawsze zamykaj połączenia, gdy tylko z nimi skończysz, aby podstawowe połączenie z bazą danych mogło wrócić do puli i być dostępne dla innych wywołujących. Pule połączeń są dość dobrze zoptymalizowane, więc nie ma za to zauważalnej kary. Rady są zasadniczo takie same jak w przypadku transakcji - po zakończeniu powinny być krótkie i zamknięte.

Sytuacja staje się bardziej skomplikowana, jeśli napotykasz problemy z usługą MSDTC, używając pojedynczej transakcji wokół kodu, który używa wielu połączeń, w takim przypadku musisz udostępnić obiekt połączenia i zamknąć go dopiero po zakończeniu transakcji.

Jednak robisz to ręcznie, więc możesz chcieć zbadać narzędzia, które zarządzają połączeniami za Ciebie, takie jak DataSets, Linq to SQL, Entity Framework lub NHibernate.

Neil Barnwell
źródło
Nie należy normalnie otwierać i zamykać połączenia w ramach każdego wywołania metody, tylko raz dla każdego żądania strony. Tego przynajmniej się nauczyłem;) Otwieranie i zamykanie kosztuje czas.
David Mårtensson,
8
@David Martensson - połączenia nie są faktycznie otwierane i zamykane, gdy wywołujesz SqlConnection.Open. ASP.NET odtwarza aktywne połączenia z puli, gdy parametry połączenia są zgodne z poprzednio używanymi parametrami połączenia. Narzut związany z tym jest nieistotny, a dodatkowo próba „zrobienia tego samodzielnie” oznacza, że ​​musisz przyjąć wszystkie zadania związane z zarządzaniem, aby zapewnić, że połączenie jest nadal aktywne przy każdym kolejnym użyciu, co zwiększa złożoność i obciążenie. W przypadku buforowania połączeń najlepszą praktyką jest otwieranie i zamykanie go przy każdym użyciu.
Jamie Treworgy
2
Z całym szacunkiem odpowiedź „Zawsze bliskie powiązania” nie pasuje do pytania… Zamykam je. Pytanie brzmi - kiedy.
Alex
@David Martensson „Raz na każdą stronę” jest zbytnio uproszczone. Masz rację, jeśli masz kilka poleceń bazy danych do wykonania jedno po drugim, możesz pozostawić otwarte połączenie podczas ich wykonywania. Zamknięcie i ponowne otwarcie wiązałoby się z niewielkim obciążeniem - połączenie trafiłoby do puli i zostało z niego odzyskane chwilę później.
Concrete Gannet
1
@David Martensson Ale nigdy nie utrzymuj bezczynnego połączenia. Jeśli czekasz na akcję użytkownika lub cokolwiek innego, zamknij ją. W razie wątpliwości zamknij ją. Otwierasz tak późno, jak tylko możesz w nadziei, że ktoś inny zakończył połączenie i połączył je. Wtedy odwzajemniasz przysługę - zamknij tak wcześnie, jak to tylko możliwe.
Concrete Gannet
13

Zastrzeżenie: wiem, że to jest stare, ale znalazłem łatwy sposób na zademonstrowanie tego faktu, więc wkładam moje dwa centy.

Jeśli nie możesz uwierzyć, że pooling będzie naprawdę szybszy, spróbuj:

Dodaj gdzieś:

using System.Diagnostics;
public static class TestExtensions
{
    public static void TimedOpen(this SqlConnection conn)
    {
        Stopwatch sw = Stopwatch.StartNew();
        conn.Open();
        Console.WriteLine(sw.Elapsed);
    }
}

Teraz wymienić wszystkie połączenia, aby Open()się TimedOpen()i uruchomić program. Teraz, dla każdego odrębnego ciągu połączenia, które posiadasz, okno konsoli (wyjściowe) będzie miało jedno długo działające otwarte i kilka bardzo szybkich otwarć .

Jeśli chcesz nadać im etykietę, możesz dodać new StackTrace(true).GetFrame(1) +do połączenia do WriteLine.

Chris Pfohl
źródło
9

Istnieją rozróżnienia między połączeniami fizycznymi i logicznymi. DbConnection jest rodzajem połączenia logicznego i wykorzystuje podstawowe połączenie fizyczne z Oracle. Zamykanie / otwieranie DbConnection nie wpływa na wydajność, ale sprawia, że ​​kod jest czysty i stabilny - w tym przypadku wycieki połączenia są niemożliwe.

Należy również pamiętać o przypadkach, w których istnieją ograniczenia dla połączeń równoległych na serwerze db - biorąc pod uwagę, że konieczne jest, aby połączenia były bardzo krótkie.

Pula połączeń uwalnia Cię od sprawdzania stanu połączeń - wystarczy je otworzyć, użyć i natychmiast zamknąć.

Tony Kh
źródło
Tak, połączenie nie jest połączeniem - tj. DbConnection nie jest połączeniem fizycznym. DbConnection to klasa .NET, która udostępnia metody i właściwości umożliwiające manipulowanie podstawowym połączeniem fizycznym.
Concrete Gannet
Niestety nie było od razu oczywiste, że wszystko to zostało zrobione w sposób dorozumiany, ale dokumentacja szczegółowo to wyjaśnia. docs.microsoft.com/en-us/dotnet/framework/data/adonet/…
Austin Salgat
2

Zwykle należy zachować jedno połączenie dla każdej transakcji (bez obliczeń równoległych)

np. kiedy użytkownik wykonuje akcję ładowania, Twoja aplikacja musi najpierw znaleźć saldo użytkownika i zaktualizować je, powinni używać tego samego połączenia.

Nawet jeśli ado.net ma swoją pulę połączeń, koszt wysyłki połączenia jest bardzo niski, ale ponowne użycie połączenia jest lepszym wyborem.

Dlaczego nie zachować tylko jednego połączenia w aplikacji

Ponieważ połączenie jest blokowane, gdy wykonujesz jakieś zapytanie lub polecenie, oznacza to, że aplikacja wykonuje tylko jedną operację bazy danych w tym samym czasie, jak niska jest wydajność.

Kolejną kwestią jest to, że Twoja aplikacja zawsze będzie miała połączenie, nawet jeśli Twój użytkownik po prostu ją otworzy, ale nie ma żadnych operacji.Jeśli jest wielu użytkowników, którzy otwierają Twoją aplikację, serwer db wkrótce będzie kosztował całe swoje źródło połączenia, podczas gdy Twoi użytkownicy nie. byle co.

ShiningRush
źródło