Tworzenie połączeń z bazą danych - Zrób to raz lub dla każdego zapytania?

101

W tej chwili tworzę połączenie z bazą danych, kiedy moja strona internetowa jest ładowana po raz pierwszy. Następnie przetwarzam stronę i uruchamiam zapytania dotyczące tej łączności. Czy to najlepszy sposób, aby to zrobić, czy powinienem tworzyć połączenie z bazą danych przy każdym uruchomieniu zapytania?

ps Bardziej sensowne jest dla mnie utworzenie 1 połączenia i użycie go, ale nie wiem, czy może to powodować inne problemy.

Używam C # (ASP.NET) z MSSQL.

webnoob
źródło

Odpowiedzi:

124

Jeśli utworzysz jeden na zapytanie / transakcję, o wiele łatwiej będzie zarządzać „zamykaniem” połączeń.

Rozumiem, dlaczego zdrowy rozsądek nakazuje, abyś otworzył i korzystał z niego przez cały czas, ale napotkasz problemy z zerwanymi połączeniami i wielowątkowością. Twoim następnym krokiem będzie otwarcie puli, powiedzmy 50, połączeń i utrzymanie ich wszystkich otwartych, rozdzielając je na różne procesy. A potem przekonasz się, że właśnie to robi dla ciebie platforma .NET .

Jeśli otworzysz połączenie, gdy jest ono potrzebne, i pozbędziesz się go po zakończeniu, to tak naprawdę nie zamknie połączenia, po prostu zwróci je do puli połączeń w celu ponownego użycia.

pdr
źródło
Właśnie czytałem ten artykuł, kiedy go opublikowałeś :) Dzięki.
webnoob
2
Strona, z którą się łączysz, jest specyficzna dla SQL Server. Czy .NET zapewnia również automatyczne buforowanie podczas łączenia z innymi bazami danych, np. Oracle, Sqlite, MySql?
briddums
@briddums - Myślę, że to zależy od złącza. Na przykład .Net nie zapewnia konektora MySql. Jest napisany i obsługiwany przez MySql. I chociaż działa, z mojego doświadczenia wynika, że ​​wcześniejsze wdrożenie było dalekie od błędów.
ZweiBlumen
1
@briddums: Zależy od zespołu dostawcy. Jestem pewien, że zarówno implementacja Oracle Microsoftu, jak i własna Oracle obsługują pule połączeń, ponieważ ich użyłem. Słyszałem, że istnieje taki MySql i spodziewam się, że dostawcy w Spring.NET będą obsługiwać pule, ale lepiej jest szukać lub pytać bezpośrednio dostawcę niż pytać mnie.
pdr
1
Powinien wiedzieć, że otwieranie, uruchamianie zapytania i rozłączanie połączenia, nawet w pętli, jest równie szybkie, a czasem SZYBCIEJ niż otwieranie go raz i zapętlanie zapytania. Zawsze po prostu pozbywaj się. Jest bardziej bezpieczny i SZYBKI. Nie martw się o obciążenie związane z uzyskaniem połączenia z puli - jest to tak banalne.
smdrager
38

Najlepszą praktyką jest utworzenie jednego połączenia na zapytanie - w przypadku wyświetlania danych najlepszą praktyką jest, aby zapytanie zawierało wszystkie potrzebne dane za jednym razem.

Informacje podstawowe:

W .NET wywoływanie SqlConnection.Open()domyślnie zawsze korzysta z puli połączeń (patrz „Korzystanie z puli połączeń z SQL Server” w MSDN). Możesz więc po prostu pobrać nowe połączenie Open()i zadzwonić, Close()gdy skończysz, a .NET zrobi to dobrze.

Pamiętaj, że bez pulowania połączeń jedno połączenie na zapytanie byłoby bardzo złym pomysłem, ponieważ tworzenie rzeczywistych połączeń z bazą danych może być bardzo kosztowne (uwierzytelnianie, obciążenie sieci itp.), A liczba jednoczesnych otwartych połączeń jest zwykle bardzo ograniczona.

Oded
źródło
7
@webnoob - Ponieważ .NET używa puli połączeń, nie robi tego. Powodem jest to, że połączenia mogą zostać zamknięte, przeniesione ponownie itp. - więc ponowne użycie połączenia nie jest dobrą praktyką.
Oded
11
-1 Odpowiedź jest raczej myląca. Tworzenie połączenia dla zapytania jest bardzo złym pomysłem. Prawdopodobnie masz na myśli „pobierz nowe połączenie dla każdego zapytania z puli połączeń” - ale to nie to samo, co utworzenie połączenia.
śleske
1
@sleske - Czym to różni się od odpowiedzi pdr?
Oded
3
@Oded: Ach, rozumiem. W .NET wywoływanie SqlConnection.Open()zawsze w przejrzysty sposób korzysta z puli połączeń. Zatem różnica między „otwieraniem połączenia” a „odzyskiwaniem połączenia z puli” nie istnieje. Moje nieporozumienie. Pozwoliłem sobie zredagować małe wyjaśnienie pytania i cofnąłem głosowanie.
śleske
2
@ eaglei22 - z pewnością powinien to zrobić (patrz docs.microsoft.com/en-us/dotnet/framework/data/adonet/… ). W ogóle, co chcesz, aby powrócić do połączenia z basenu jak najszybciej, chociaż, jeśli wydając szereg zapytań w kolejności, to może lepiej byłoby ponowne połączenie jak sugerujesz. Musisz przetestować i sprawdzić, które podejście jest dla Ciebie lepsze (nie wiem, jakich kryteriów używasz - sprawdź oba sposoby i zobacz wpływ na wybrane wskaźniki).
Oded
0

Pamiętaj, że wszystko to w kontekście ekosystemu .Net.

Czasami programiści chcą „zoptymalizować” swój kod, aby ponownie użyć obiektów połączenia. Biorąc pod uwagę kontekst tego pytania, prawie zawsze jest to błąd.

ADO.Net ma funkcję o nazwie Pula połączeń . Kiedy tworzysz i otwierasz nowy obiekt połączenia, tak naprawdę robisz to, żądając połączenia z puli. Po zamknięciu połączenia zwracane jest ono do puli.

Ważne jest, aby zrozumieć obiektów używamy bezpośrednio w kodzie: SqlConnection, MySqlConnection, OleDbConnectio, etc, są tylko owijarki wokół prawdziwego bazowego połączenia zarządzanego przez ADO.Net, a rzeczywiste połączenia ADO.NET są znacznie „cięższy” i droższe z punktu widzenia wydajności. Są to leżące u podstaw obiekty, które mają obawy, takie jak uwierzytelnianie, tranzyt sieci, szyfrowanie i te rzeczy znacznie przewyższają niewielką ilość pamięci w obiekcie, który faktycznie widzisz we własnym kodzie.

Podczas próby ponownego użycia obiektu połączenia przerywa się zdolność ADO.Net do skutecznego zarządzania ważnymi połączeniami bazowymi. Zyskujesz wydajność w małej rzeczy kosztem znacznie większej rzeczy.

Ponowne użycie połączenia w aplikacji lub żądaniu HTTP może również zmusić Cię do przypadkowej serializacji czegoś, co w innym przypadku mogłoby być uruchomione równolegle i stać się wąskim gardłem wydajności. Widziałem to w prawdziwych aplikacjach.

W przypadku przykładu strony internetowej tutaj, gdzie przynajmniej utrzymujesz małe połączenie przez czas trwania pojedynczego żądania / odpowiedzi http, możesz zyskać jeszcze większą wydajność, oceniając, jakie zapytania uruchamiasz w potoku żądań, i spróbuj uzyskać je do jak najmniejszej liczby oddzielnych żądań do bazy danych (wskazówka: możesz przesłać więcej niż jedno zapytanie w jednym ciągu SQL i używać DataReader.NextResult()lub sprawdzać różne tabele w DataSetcelu przemieszczania się między nimi).

Innymi słowy, zamiast myśleć w kategoriach ponownego wykorzystania jednego połączenia dla aplikacji lub żądania HTTP w porównaniu do jednego połączenia na zapytanie, pomyśl w kategoriach jednego połączenia za każdym razem, gdy wywołujesz bazę danych ... podczas każdej podróży w obie strony. Następnie spróbuj zminimalizować liczbę połączeń, minimalizując liczbę tych podróży. W ten sposób możesz osiągnąć oba cele.


Ale to tylko jeden rodzaj optymalizacji. Istnieje również optymalizacja czasu programisty i efektywne ponowne wykorzystanie kodu. Deweloperzy nie chcą ciągle pisać tego samego kodu, aby uzyskać obiekt połączenia, który jest otwarty i gotowy do użycia. To nie tylko żmudne, to sposób na wprowadzenie błędów w programie.

Jednak nawet tutaj generalnie lepiej jest mieć jedno połączenie na zapytanie (lub w obie strony). Istnieją inne wzorce, których można użyć, aby uniknąć ponownego zapisywania tego samego kodu szablonu. Oto jeden przykład, który mi się podoba, ale jest wiele innych.

Joel Coehoorn
źródło
Jestem spóźniony na to przyjęcie, ale myślę, że ta odpowiedź obejmuje kilka ważnych kwestii :)
Joel Coehoorn