Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Skalowalny serwer korzystający z baz danych - jakie biblioteki i wzorce

Ostatnio zmodyfikowano 2019-11-11 22:49
Autor Wiadomość
RazzorFlame
Temat założony przez niniejszego użytkownika
Skalowalny serwer korzystający z baz danych - jakie biblioteki i wzorce
» 2019-11-11 17:54:35
Cześć,
piszę serwer i zastanawiam się w jaki sposób zaprojektować komunikację z bazą danych. Póki co robiłem np. tak:
C/C++
namespace db
{
    class Player
    {
        std::string nick;
        int level;
        int strength;
       
        // ... etc
        struct ChangesStatus {
            bool nick = false;
            bool stats = false;
        } mutable changesStatus;
    };
   
    // Zapisuje zmiany korzystajac z Player::changesStatus
    void save( DatabaseConnection & con_, Player const & player_ );
}

class Player
{
public:
    void setNick( std::string newNick_ ) {
        dbInterface.nick = std::move( newNick_ );
        dbInterface.changesStatus.nick = true;
    }
    // ...
    db::Player dbInterface;
};
Mam też taką prostą klasę cache-ującą te wyniki, która ładuje to z bazy danych i w co jakiś czas zapisuje zmiany.
Jest tam trochę więcej zabezpieczeń, ale mniej więcej tak to działa. Jest to w miarę skalowalne, ale jak się dopiero w praktyce okazało - bardzo czasochłonne i monotonne. Nie jestem wirtuozem architektur serwerowych dlatego chciałbym się od kogoś bardziej doświadczonego dowiedzieć jak to się robi profesjonalnie. Mam sporo klas, które są stale zapisywane do i ładowane z bazy danych i chciałbym to robić bezpiecznie (np. wspierać usunięcie danego rekordu w dowolnym momencie i też nie powielać wczytanego rekordu w pamięci, bo to może prowadzić do niespójności) i też w miarę wygodnie.
Jakich bibliotek powinienem użyć poza zwykłym driverem bazy danych i z jakich wzorców projektowych skorzystać? Chciałbym też zerknąć na kod jakiejś appki, która robi to dobrze.
Dzięki wielkie :)

Edit:
Zastanawiam się, czy tworzenie czegoś takiego jak ChangesStatus dla rekordów, które składają się z kilku - max kilkunastu pól ma jakiś sens. Nie wiem do jakiego stopnia bazy danych są zoptymalizowane, więc założyłem, że ja się tym zajmę. Dobrze robię?

Edit #2:
Problem z tym kodem jest jeszcze taki, że przez to, że staram się aby każda klasa była funkcjonalna sama w sobie, to powielam takie metody jak np. tutaj setNick. W praktyce to wygląda tak:
C/C++
namespace db
{
    class Player
    {
        void changeNick( std::string newNick_ ) {
            if( nick != newNick_ ) {
                nick = newNick_;
                changesStatus.nick = true;
            }
        }
        std::string nick;
        // ... etc
    };
}

class Player
{
public:
    void setNick( std::string newNick_ ) {
        dbInterface.changeNick( std::move( newNick_ ) );
    }
    // ...
};
P-175537
DejaVu
» 2019-11-11 18:54:23
Teoria mówi, że raczej bazę danych się skaluje. Przykład:
- stawiasz 10 takich samych serwisów, które strzelają do bazy danych
- baza danych nie wyrabia? To skalujesz bazę danych (która ma te mechanizmy już zazwyczaj zapewnione).

Czy to jest efektywne rozwiązanie? Szczerze mówiąc, to odpowiedź zależy od rozpatrywanego problemu. Jeżeli chcesz skalować usługę poprzez dodawanie serwisów (czyli w praktyce dokładanie serwerów), to wówczas jakiekolwiek mechanizmy cache-ujące będą ci bardzo utrudniały życie, jeżeli baza danych jest modyfikowana.

Jeżeli celujesz w jeden wydajny serwis (plus baza danych), to cache-owanie danych w RAM może być całkiem dobrą opcją, jeżeli wykonujesz dużo odczytów i zapisów, ponieważ komunikacja z bazą danych zajmuje trochę czasu.

Jak fajnie zorganizować kod z cache-owaniem danych? W sumie nad podobnym problemem zastanawiam się w domowym projekcie :)
P-175539
pekfos
» 2019-11-11 18:55:22
Brakuje mi tu jakiegoś ID gracza (bo raczej masz więcej niż jednego). Możesz zrobić tak, że masz klasę Player, reprezentującą jednego gracza i klasę PlayersCache, która zarządza wszystkimi graczami. Zamiast pilnować, czy zmienił się nick, czy level gracza, trzymaj tylko informację, czy cokolwiek się tam zmieniło. Aktualizuj cały wiersz tabeli, żeby się za bardzo nie narobić. Jeśli kiedyś wyjdzie, że ma to negatywny wpływ na wydajność, to zawsze możesz zoptymalizować poszczególne przypadki. setNick() wystarczy żeby było na Player, klasa do cache bazy będzie wiedzieć, że coś się zmieniło.
P-175540
RazzorFlame
Temat założony przez niniejszego użytkownika
» 2019-11-11 19:41:48
@DejaVu uświadomiłeś mi, że to cache-owanie może być bardzo niebezpieczne. Będę dodawał później co najmniej stronę internetową, która też będzie mogła modyfikować bazę danych (nie jakoś bardzo, ale jednak).
Doszedłem do wniosku, że najbezpieczniej jest korzystać bezpośrednio na wartościach z bazy danych. To pewnie jednak zabije wydajność.
Drugim pomysłem na jaki wpadłem, który (chyba) jest bezpieczny i wydajny to stworzenie "nakładki" na bazę danych, takiego pośrednika między każdym korzystającym z bazy danych a samą bazą. Wtedy mógłbym udostępnić własny interfejs, w formie C++-sowych klas i funkcji, które omijałyby koszt wykonywania zapytań do bazy danych, robiąc to tylko raz na jakiś czas, przez co problem z wydajnością by się zniwelował. Wtedy operowałbym zawsze bezpośrednio na wartościach oferowanych przez tą "nakładkę".
To rozwiązanie pewnie będzie cholernie czasochłonne i w końcu już nie wiem jak to zrobić. Nie chcę zatrzymywać projektu na tydzień przez coś takiego :| Kurde, ktoś na pewno już się z tym zmagał. Nie wiem za bardzo czego szukać.

@pekfos ten brak id to niedopatrzenie, pisałem to z palca. Takie rozwiązanie z cacheowaniem już mam. Problem mam z objętością kodu odpowiadającą za zarządzanie tym cachem, dlatego szukam jakiejś libki albo innego, lepszego sposobu, na który może kto inny wpadł.

Edit:
Raczej będę się ogarniczał do tego, że wszystkie clienty bazy danych będą na jednej maszynie, więc takie RPC by były szybkie. Musiałbym pewnie też podzielić dane na te ważne, które muszą koniecznie być dobrze zsynchronizowane i te, które są mało istotne, które można zsynchronizować po chwili, nawet jeśli jakaś niewłaściwa wartość będzie mogła się pojawić.
P-175541
DejaVu
» 2019-11-11 22:49:19
Polecam najpierw zrobić produkt ze strzałami do bazy danych, a potem zastanawiać się co optymalizować, jeżeli wiesz, że ruch będzie duży. Przedwczesna optymalizacja dużo kosztuje energii i czasu... potrafi nawet zabić projekt zanim powstanie.
P-175542
« 1 »
  Strona 1 z 1