To pytanie dotyczy najlepszych praktyk w architekturze.
Nasza obecna architektura
Mam klasę PHP, która uzyskuje dostęp do MySQL w celu uzyskania informacji o użytkowniku. Nazwijmy to User
. User
jest uzyskiwany wielokrotnie, dlatego zaimplementowaliśmy warstwy buforowania, aby zmniejszyć obciążenie.
Pierwsza warstwa to pamięć podręczna „na żądanie”. Po pobraniu danych z MySQL przechowujemy je w prywatnej własności User
. Wszelkie kolejne żądania danych zwracają właściwość zamiast ponownie żądać danych z MySQL.
Ponieważ żądanie sieciowe żyje i umiera na zasadzie na żądanie, ta pamięć podręczna uniemożliwia aplikacji dostęp do MySQL więcej niż raz w jednym żądaniu.
Nasza druga warstwa to Memcached. Gdy własność prywatna jest pusta, najpierw sprawdzamy dane Memcached. Jeśli Memcached jest pusty, pytamy MySQL o dane, aktualizujemy Memcached i aktualizujemy prywatną własność User
.
Pytanie
Nasza aplikacja jest grą i czasami konieczne jest, aby niektóre dane były jak najbardziej aktualne. W ciągu około pięciu minut żądanie odczytu danych użytkownika może nastąpić 10 lub 11 razy; wtedy może wystąpić aktualizacja. Kolejne żądania odczytu muszą być aktualne, inaczej mechanika gry zawiedzie.
Tak więc, co zrobiliśmy, to zaimplementowanie fragmentu kodu, który jest wykonywany, gdy nastąpi aktualizacja bazy danych. Ten kod ustawia klucz w Memcached ze zaktualizowanymi danymi, więc wszystkie kolejne żądania do Memcached są aktualne.
Czy to jest optymalne? Czy są jakieś obawy związane z wydajnością lub inne „problemy”, o których powinniśmy pamiętać, próbując utrzymać coś w rodzaju „żywej pamięci podręcznej”?
źródło
Odpowiedzi:
Moje zalecenie to sprawdzenie profilu użytkowania i wymagań dotyczących pamięci podręcznej.
Nie widzę powodu, dla którego miałbyś pozostawić nieaktualne dane w memcached. Myślę, że wybrałeś właściwe podejście, tj .: zaktualizuj bazę danych.
W każdym razie będziesz potrzebować opakowania aktualizacji DB (co już zrobiłeś). Twój kod, aby zaktualizować użytkownika w DB i in-RAM, powinien również wykonać push na memcached lub wygaśnięcie w memcached.
Na przykład - jeśli użytkownicy zwykle wykonują aktualizację raz na sesję w ramach wylogowania, aktualizacja danych w pamięci podręcznej nie ma większego sensu (np. Łączny wysoki wynik) - należy natychmiast ją wygasnąć.
JEŚLI jednak zamierzają zaktualizować dane (np. Bieżący stan gry), a następnie 0,2 sekundy później otrzymasz natychmiastowe trafienie strony PHP, które zażąda danych, chciałbyś, aby były one świeże w pamięci podręcznej.
źródło
Nie zrobiłbym tego tak, jak ty nakreśliłeś. Musisz zdecydować, czy rzeczywiście POTRZEBUJESZ całkowicie aktualnych danych. Następnie, jeśli potrzebujesz, zdecyduj, które części danych muszą być zawsze aktualne i oddziel je od rzeczy, które mogą być buforowane w Twojej architekturze.
Na przykład prawdopodobnie chcesz zaktualizować adres e-mail użytkownika, gdy tylko go zmieni, więc nie wysyłasz wiadomości e-mail na zły adres, ale jest mało prawdopodobne, że data urodzenia lub nazwisko użytkownika będzie musiało być całkowicie aktualne, aby zapewnić przyzwoitą jakość obsługi. (Uwaga: nie korzystam z przykładu architektury gier, ponieważ nie wiem, do jakiego rodzaju gry należy dążyć, i myślę, że ta gra jest dość łatwa do zrozumienia).
W ten sposób masz dwa wyraźne zestawy danych: krótko- i długoterminowe dane do buforowania. Prawdopodobnie możesz uciec z pamięci podręcznej trwającej około minuty na danych krótkoterminowych, aby zwolnić obciążenie bazy danych, ale dane długoterminowe można pozostawić w pamięci podręcznej na tak długo, jak długo używany.
Następnie musisz poradzić sobie z aktualizacjami. Najpierw przyjrzałbym się użyciu wyzwalacza DB, aby po prostu usunąć elementy z pamięci podręcznej, gdy są nieaktualne. Zmusi to warstwę biznesową do uruchomienia odświeżania pamięci podręcznej przy następnym żądaniu danych, zwalniając miejsce w pamięci podręcznej, jeśli dane nie są używane (na przykład, jeśli użytkownik zmieni swój adres e-mail, a następnie natychmiast się wyloguje) . Jeśli spowoduje to problemy z wydajnością w interfejsie użytkownika (tj. Wprowadzi zbyt duże opóźnienie podczas oczekiwania na odświeżenie pamięci podręcznej), możesz po prostu uruchomić wywołanie pamięci podręcznej po usunięciu elementu z pamięci podręcznej. Chciałbym również zoptymalizować czasy odczytu DB dla tego małego zestawu danych, aby upewnić się, że wszelkie opóźnienia spowodowane odświeżaniem pamięci podręcznej są minimalne (powinno to być łatwiejsze, ponieważ wystarczy załadować tylko te dane, których naprawdę potrzebujesz).
W żadnym wypadku nie chciałbym dodać dodatkowej metody zapełniania pamięci podręcznej, ponieważ wtedy będziesz musiał utrzymywać wywołanie (i zaczepy API itp.) W dwóch miejscach.
Jeśli chodzi o gotchas, najważniejszą rzeczą, na którą musisz uważać, jeśli piszesz bezpośrednio do pamięci podręcznej, jest synchronizacja. Jeśli wiele wątków próbuje czytać podczas cichej aktualizacji, możesz mieć poważne problemy z nieprawidłowymi danymi, co w pierwszej kolejności może uniemożliwić aktualizację danych.
źródło