Optymalizacja pod multicore

0

Cześć,
piszę sobie WebApi, które łączy się z bazą danych (DB) za pomocą specjalnie do tego celu utworzonego konta w DB. Nie wiem jak jest w innych bazach danych, ale zdaje się, że w Postgresie jedno połączenie korzysta z jednego rdzenia. Jeśli mam serwer, który ma 8 rdzeni to (jeśli dobrze to rozumiem) 7 będzie niewykorzystywanych. Czy faktycznie jest tak jak piszę?
Czy w takiej sytuacji pisząc WebApi powinienem utworzyć 8 kont w bazie danych oraz utworzyć 8 dbcontext-ów i losowo je wybierać przy pobieraniu danych z DB?
Czy to jest praktykowane? Nie kojarzę, aby w jakimś tutorialu ktoś wspominał o tym problemie a wydaje się on dość istotny.
Jakie jest wasze zdanie na ten temat?

4

Masz tak dużo danych, że martwisz się o wydajność? Masz zamiar robić jakieś skompliowane operacje na tej bazie/serwerze? Moim zdaniem szkoda czasu na takie bzdety 🤔

0
AdamWox napisał(a):

Masz tak dużo danych, że martwisz się o wydajność? Masz zamiar robić jakieś skompliowane operacje na tej bazie/serwerze? Moim zdaniem szkoda czasu na takie bzdety 🤔

Nie, nie mam problemu z wydajnością, ale jak coś robię to chciałbym to robić dobrze, żeby nie musieć się zamartwiać w przyszłości i refaktoryzować cały projekt bo nie pomyślałem o czymś na początku.

4

To nie ma nic wspólnego z ilością rdzeni, tylko z twoim programowaniem. Nie jesteś w stanie przewidzieć wszystkiego, ale zapewniam cię, że o optymalizacje na podstawie rdzeni nie musisz się martwić. Te systemy są na tyle dobrze zaprojektowane i sprawnie działające, że tylko ty kodem możesz tą wydajność "popsuć".

6

Nie wiem jak jest w innych bazach danych, ale zdaje się, że w Postgresie jedno połączenie korzysta z jednego rdzenia

9.6+ wykorzystuje wiele rdzeni na połączenie (https://wiki.postgresql.org/wiki/FAQ#How_does_PostgreSQL_use_CPU_resources.3F).

0

A to tylko jedno połączenie ciągle będzie? Nie wiem jak to jest w C# ale np w takim node.js robi się pulę połączeń do wykorzystywania przez requesty, bo inaczej wszystkie zapytania do bazy by się zakolejkowały na tym jednym połączeniu.

3
Kofcio napisał(a):

Nie, nie mam problemu z wydajnością, ale jak coś robię to chciałbym to robić dobrze, żeby nie musieć się zamartwiać w przyszłości i refaktoryzować cały projekt bo nie pomyślałem o czymś na początku.

E.D.Knuth:

Przedwczesna optymalizacja jest źródłem wszelkiego (lub przynajmniej większości) zła w programowaniu.

Owszem jak ci ta optymalizacja zajmie co najwyżej kilka/kilkanaście minut więcej niż bez niej to warto.
Tą granicę minutową sam sobie musisz dobrać.
Ale jeżeli masz poprawnie działające przeczucia i masz mocne przeczucie że jednak trzeba optymalizować teraz zaraz to ... sam decyduj :D

5

Zaraz, zaraz. Chcesz powiedzieć, że otwierasz jedno połączenie do DB i każde zapytanie do DB idzie na tym połączeniu i zamykasz je dopiero jak serwer wyłączasz? Przecież połączenie do DB powinno być otwarte tylko na czas grzebania w bazie i zaraz zamykane. Co więcej jak podczas trwania jednego zapytania przyjdzie ci inne to przecież nie czekasz aż się to pierwsze skończy tylko odpalasz drugie połączenie i tyle. Przecież to podstawy pracy z DB równolegle.

9

Jak wyżej. Po prostu otwieraj połączenia i je "zamykaj". W cudzysłowie bo w połączeniu z bazami danych jest używane connection pooling, wywołanie Close nie zamyka fizycznie połączenia a jedynie zwraca je do puli (ponowne tworzenie połączenia TCP za każdym razem byłoby zbyt powolne).
W przypadku postgresa w connection stringu chyba musisz przekazać Pooling=true i wszystkim się zajmuje framework i driver, nie musisz się przejmować. W przypadku DbContextów po prostu tworzysz je za każdym razem przy każdym zapytaniu o nic się nie martwiąc. Nie rób własnej puli DbContextów i ich nie przechowuj, wszystko się dzieje magicznie pod spodem.

DbContext powinien żyć możliwie najkrócej jak się da. DbContext = Unit of work
Rozumiem że próbowałeś to zoptymalizować przechowując statycznie DbContext wbrew kodowi który znalazłeś w tutorialach myśląc że robisz dobrze i wprowadzasz optymalizację, a w rzeczywistości uzyskując efekt odwrotny.

Najlepiej wstrzykuj DbContext jako zależność scoped lub IDbContextFactory jeśli chcesz mieć większą kontrolę nad tworzeniem i zwalnianiem kontekstów
https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#dbcontext-in-dependency-injection-for-aspnet-core
Podążaj za wskazówkami z dokumentacji to nie zginiesz, nie rób własnych prób optymalizacji dopóki nie rozumiesz jak wszystko faktycznie działa i/lub bez faktycznych pomiarów.

1

Podchodzisz do tego po prostu jakoś dziwnie.

  1. Z tego co pamiętam to w Postgresie jedno konto użytkownika może obsługiwać wiele połączeń (można ustalić limit), więc wiele wątków może uderzać do tej samej bazy danych za pomocą tego samego usera.
  2. Jeśli już baza danych ogarnia "jeden użytkownik = wiele połączeń", to pozostaje kwestia serwera. Tutaj masz po prostu do ogarnięcia ConnectionPool: https://en.wikipedia.org/wiki/Object_pool_pattern#C# . Nie wiem, jak to jest w C#, ale wiem, że na 100% istnieją gotowe rozwiązania do tego. Wtedy każdy wątek po stronie serwera może podbijać do warstwy pośredniczącej, która połączeniami będzie zarządzać.
2

Z tego co pamiętam to każdy sensowny provider ADO.NET ma wbudowany connection pooling i raczej domyślnie jest włączony.
Wtedy wywoływanie Close czy tam Dispose nie powoduje zamknięcia fizycznego połączenia do bazy tylko jego zwrócenie do puli. To kiedy połączenia są zamykane to już szczegół implementacyjny każdego z providerów, ale jestem przekonany, że trzymają one jakąś minimalną liczbę otwartych połączeń.

Entity Framework do łączenia się z konkretną bazą korzysta z ww. providerów, więc na poziomie EF nie musisz nic robić. Tworzenie puli DbContextów i ich losowanie nic nie zmieni.

0

Dziękuję wszystkim za odpowiedź i ciekawą dyskusję a raczej informacje.
Wydawało mi się, że gdy jest mowa o połączeniu z bazą danych chodzi o konto usera w DB (w ConnectionString podajemy m.in. login i hasło) i te połączenie jest stałe - przynajmniej przez piewien czas). Po dej dyskusji, jeśli dobrze rozumiem, my za każdym razem musimy wysłać do bazy ConnectionString -> czyli zalogować się / nawiązać połączenie -> wykonać zapytanie -> i automatycznie się rozłączamy. Zgadza się?

3

Nie, zupełnie się nie zgadza.
Wysyłasz ConnectionString jako parametr do DB providera, ten tworzy połączenie raz dla każdego connectionstringa i go używa ponownie dopóki może. Jeśli równolegle przyjdzie kolejny request to utworzone zostanie drugie połączenie i nadal trzymane w puli dla kolejnych.

0
obscurity napisał(a):

Nie, zupełnie się nie zgadza.
Wysyłasz ConnectionString jako parametr do DB providera, ten tworzy połączenie raz dla każdego connectionstringa i go używa ponownie dopóki może. Jeśli równolegle przyjdzie kolejny request to utworzone zostanie drugie połączenie i nadal trzymane w puli dla kolejnych.

Ok, dziękuję za wyjaśnienie. A czy ten ConnectionString jest za każdym razem wysyłany do DB i to DB analizuje czy połączenie z tym CS jest utworzone czy robi to ORM tj. np. EF lub Dapper?

1 użytkowników online, w tym zalogowanych: 0, gości: 1