MemoryStoreService to usługa danych o wysokiej przepustowości i niskim opóźnieniu, która zapewnia szybkie przechowywanie danych w pamięci dostępne z wszystkich serwerów w aktywnej sesji. Pamięci przechowywania są odpowiednie dla częstych i efemerycznych danych, które zmieniają się szybko i nie muszą być trwałe, ponieważ są szybsze do uzyskania dostępu i znikają po osiągnięciu maksymalnego czasu życia. Do danych, które muszą być przechowywane przez sesje, użyj przechowywań danych.
Struktury danych
Zamiast bezpośrednio uzyskiwać dostęp do surowych danych, pamięci przechowywania mają trzy podstawowe struktury danych współdzielone między serwerami dla szybkiego przetwarzania: posortowana mapa, kolejka i mapa haszowa. Każda struktura danych jest dobrze dopasowana do określonych przypadków użycia:
- Dopasowywanie bazujące na umiejętnościach - Zapisz informacje o użytkowniku, takie jak poziom umiejętności, w współdzielonej kolejce między serwerami, i użyj serwerów lobby do okresowego uruchamiania dopasowywania.
- Handel i aukcje międzyserwerowe - Umożliw uniwersalny handel między różnymi serwerami, gdzie użytkownicy mogą licytować przedmioty z cenami zmieniającymi się w czasie rzeczywistym, za pomocą posortowanej mapy par klucz-wartość.
- Globalne tablice wyników - Przechowuj i aktualizuj rankingi użytkowników na współdzielonej tablicy wyników wewnątrz posortowanej mapy.
- Wspólne ekwipunki - Zapisz przedmioty ekwipunku i statystyki w współdzielonej mapie haszowej, gdzie użytkownicy mogą równocześnie korzystać z przedmiotów ekwipunku.
- Bufor dla danych trwałych - Synchronizuj i kopiuj swoje dane trwałe w przechowywaniu danych do pamięci mapy haszowej, która może działać jako pamięć podręczna i poprawić wydajność Twojej gry.
Ogólnie, jeśli potrzebujesz uzyskać dostęp do danych na podstawie konkretnego klucza, użyj mapy haszowej. Jeśli potrzebujesz, aby te dane były uporządkowane, użyj posortowanej mapy. Jeśli potrzebujesz przetwarzać swoje dane w określonym porządku, użyj kolejki.
Limity i kwoty
Aby utrzymać skalowalność i wydajność systemu, pamięci przechowywania mają limity użytkowania danych dla rozmiaru pamięci, żądań API i rozmiaru struktury danych.
Pamięci przechowywania mają politykę usuwania opartą na czasie wygaśnięcia, znaną również jako czas życia (TTL). Przedmioty są usuwane po upływie terminu, a limit pamięci zwalnia się na nowe wpisy. Gdy osiągniesz limit pamięci, wszystkie następne żądania zapisu zawiodą, dopóki przedmioty nie wygaśnie lub nie usuniesz ich ręcznie.
Kwota rozmiaru pamięci
Kwota pamięci ogranicza całkowitą ilość pamięci, którą gra może zużyć. To nie jest stała wartość; zamiast tego zmienia się w czasie w zależności od liczby użytkowników w grze zgodnie ze wzorem 64KB + 1.2KB * [liczba użytkowników]. Kwota ma zastosowanie na poziomie gry, a nie na poziomie serwera.
Kiedy użytkownicy dołączają do gry, dodatkowa kwota pamięci jest dostępna natychmiast. Gdy użytkownicy opuszczają grę, kwota nie zmniejsza się natychmiast. Istnieje okres przejrzystości wynoszący osiem dni, przed ponowną oceną kwoty na niższą wartość.
Po osiągnięciu przez Twoją grę kwoty rozmiaru pamięci żadne żądania API, które zwiększają rozmiar pamięci, nie powiodą się. Żądania, które zmniejszają lub nie zmieniają rozmiaru pamięci, nadal będą się powieść.
Za pomocą panelu obserwowalności możesz na bieżąco przeglądać kwotę rozmiaru pamięci swojej gry, korzystając z wykresu Zużycie pamięci.
Limity żądań API
Kwota jednostki żądania ma zastosowanie do wszystkich wywołań API MemoryStoreService. Ta kwota wynosi 1000 + 120 * [liczba jednoczesnych użytkowników] jednostek żądań na minutę.
Większość wywołań API zużywa tylko jedną jednostkę żądania, z kilkoma wyjątkami:
MemoryStoreSortedMap:GetRangeAsync()
Zużywa jednostki w zależności od liczby zwróconych przedmiotów. Na przykład, jeśli ta metoda zwraca 10 przedmiotów, wywołanie liczy się jako 10 jednostek żądań. Jeśli zwraca pustą odpowiedź, liczy się jako jedna jednostka żądania.
Zużywa jednostki w zależności od liczby zwróconych przedmiotów, tak jak MemoryStoreSortedMap:GetRangeAsync(), ale zużywa dodatkową jednostkę co dwa sekundy podczas odczytu. Określ maksymalny czas odczytu za pomocą parametru waitTimeout.
MemoryStoreHashMap:UpdateAsync()
Zużywa minimum dwie jednostki.
MemoryStoreHashMap:ListItemsAsync()
Zużywa [liczba partycji skanowanych] + [przedmioty zwrócone] jednostek.
Kwota żądań jest również stosowana na poziomie gry, a nie na poziomie serwera. Zapewnia to elastyczność w przydzielaniu żądań między serwerami, pod warunkiem, że całkowita szybkość żądań nie przekracza kwoty. Jeśli przekroczysz kwotę, otrzymasz odpowiedź błędną, gdy usługa ograniczy Twoje żądania.
Dzięki funkcji obserwowalności możesz na bieżąco przeglądać kwotę jednostek żądania swojej gry.
Limity rozmiaru struktury danych
Dla pojedynczej posortowanej mapy lub kolejki obowiązują następujące limity rozmiaru i liczby przedmiotów:
- Maksymalna liczba przedmiotów: 1,000,000
- Maksymalny całkowity rozmiar (w tym klucze dla posortowanej mapy): 100 MB
Limity na partycje
Zobacz limity na partycje.
Najlepsze praktyki
Aby utrzymać optymalny wzór użycia pamięci i unikać przekraczania limitów, stosuj się do poniższych najlepszych praktyk:
Usuwaj przetworzone przedmioty. Systematyczne czyszczenie przeczytanych przedmiotów za pomocą metody MemoryStoreQueue:RemoveAsync() dla kolejek i MemoryStoreSortedMap:RemoveAsync() dla posortowanych map może zwolnić pamięć i utrzymać aktualność struktury danych.
Ustaw czas wygaśnięcia na jak najkrótszy, gdy dodajesz dane. Chociaż domyślny czas wygaśnięcia wynosi 45 dni zarówno dla MemoryStoreQueue:AddAsync(), jak i MemoryStoreSortedMap:SetAsync(), ustawienie jak najkrótszego czasu może automatycznie oczyścić stare dane, aby zapobiec ich zapełnianiu Twojej kwoty zużycia pamięci.
- Nie przechowuj dużej ilości danych z długim czasem wygaśnięcia, ponieważ zagraża to przekroczeniu Twojej kwoty pamięci i potencjalnie spowoduje problemy, które mogą zrujnować całą Twoją grę.
- Zawsze albo wyraźnie usuń niepotrzebne przedmioty, albo ustaw krótki czas wygaśnięcia przedmiotu.
- Ogólnie rzecz biorąc, powinieneś używać wyraźnego usuwania do zwalniania pamięci, a czasu wygaśnięcia przedmiotu jako mechanizmu zabezpieczającego, aby zapobiec zapełnianiu pamięci przez nieużywane przedmioty przez długi czas.
Zachowuj tylko niezbędne wartości w pamięci.
Na przykład dla gry w dom aukcyjny potrzebujesz tylko utrzymać najwyższą ofertę. Możesz użyć MemoryStoreSortedMap:UpdateAsync() na jednym kluczu, aby utrzymać najwyższą ofertę, zamiast trzymać wszystkie oferty w swojej strukturze danych.
Użyj ekspansyjnego opóźnienia, aby pomóc pozostać poniżej limitów żądań API.
Na przykład, jeśli otrzymasz DataUpdateConflict, możesz spróbować ponownie po dwóch sekundach, następnie czterech, ośmiu itd. zamiast ciągłego wysyłania żądań do MemoryStoreService, aby uzyskać poprawną odpowiedź.
Podziel ogromne struktury danych na mniejsze za pomocą shardingu.
Często łatwiej jest zarządzać danymi w mniejszych strukturach, niż przechowywać wszystko w jednej dużej strukturze danych. Podejście to może również pomóc uniknąć limitów użytkowania i szybkości. Na przykład, jeśli masz posortowaną mapę, która używa prefiksów dla swoich kluczy, rozważ oddzielenie każdego prefiksu do własnej posortowanej mapy. Dla szczególnie popularnej gry możesz nawet oddzielić użytkowników do wielu map na podstawie ostatnich cyfr ich identyfikatorów użytkownika.
Sharduj często dostępne klucze w mapach haszowych, mając kilka kopii klucza, aby rozdzielić obciążenie.
Kompresuj przechowywane wartości.
Na przykład, rozważ użycie algorytmu LZW w celu zmniejszenia rozmiaru przechowywanych wartości.
Zapisz się do Rozszerzonych Usług.
Możesz zwiększyć swoje limity przechowywania i żądań, przystępując do Rozszerzonych Usług.
Obserwowalność
Panel Obserwowalności zapewnia wgląd i analizy do monitorowania i rozwiązywania problemów z użyciem pamięci przechowywania. Dzięki wykresom z aktualizacjami w czasie rzeczywistym na różnych aspektach zużycia pamięci i żądań API możesz śledzić wzór zużycia pamięci swojej gry, przeglądać aktualnie przyznane kwoty, monitorować status API i identyfikować potencjalne problemy w celu optymalizacji wydajności.
Poniższa tabela zawiera listę i opis wszystkich kodów statusu odpowiedzi API dostępnych w wykresach Liczba żądań według statusu i Żądania według API x Status na panelu obserwowalności. Aby uzyskać więcej informacji na temat rozwiązywania tych błędów, zobacz Rozwiązywanie problemów. Aby uzyskać konkretny limit lub kwotę, do której odnosi się błąd, zobacz Limity i Kwoty.
| Kod statusu | Opis |
|---|---|
| Sukces | Sukces. |
| DataStructureMemoryOverLimit | Przekracza limit rozmiaru pamięci na poziomie struktury danych (100MB). |
| DataUpdateConflict | Konflikt z powodu równoczesnej aktualizacji. |
| AccessDenied | Brak uprawnień do dostępu do danych gry. To żądanie nie zużywa jednostek żądań ani nie wykorzystuje kwoty. |
| InternalError | Błąd wewnętrzny. |
| InvalidRequest | Żądanie nie zawiera wymaganych informacji lub zawiera błędne informacje. |
| DataStructureItemsOverLimit | Przekracza limit liczby przedmiotów na poziomie struktury danych (1M). |
| NoItemFound | Nie znaleziono przedmiotu w MemoryStoreQueue:ReadAsync() lub MemoryStoreSortedMap:UpdateAsync(). ReadAsync() sprawdza co 2 sekundy i zwraca ten kod statusu, aż znajdzie przedmioty w kolejce. |
| DataStructureRequestsOverLimit | Przekracza limit jednostek żądań na poziomie struktury danych (100,000 jednostek żądań na minutę). |
| PartitionRequestsOverLimit | Przekracza limit jednostek żądań na partycję. |
| TotalRequestsOverLimit | Przekracza limit jednostek żądań na poziomie uniwersum. |
| TotalMemoryOverLimit | Przekracza limit pamięci na poziomie uniwersum. |
| ItemValueSizeTooLarge | Rozmiar wartości przekracza limit (32KB). |
Poniższa tabela zawiera stany kodów z strony klienta, które obecnie nie są dostępne na panelu obserwowalności.
| Kod statusu | Opis |
|---|---|
| InternalError | Błąd wewnętrzny. |
| UnpublishedPlace | Musisz opublikować to miejsce, aby użyć MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService musi być wywoływana z serwera. |
| InvalidExpirationTime | Pole 'czas wygaśnięcia' musi być między 0 a 3,888,000. |
| InvalidRequest | Nie można przekonwertować wartości na json. |
| InvalidRequest | Nie można przekonwertować sortKey na ważną liczbę lub ciąg. |
| TransformCallbackFailed | Nie udało się wywołać funkcji zwrotnej transformacji. |
| RequestThrottled | Niedawne żądania MemoryStores osiągnęły jedno lub więcej limitów. |
| UpdateConflict | Przekroczono maksymalną liczbę prób. |
Rozwiązywanie problemów
Poniższa tabela zawiera listę i opis zalecanych rozwiązań dla każdego kodu statusu odpowiedzi:
| Błąd | Opcje rozwiązywania problemów |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
|
| Błąd wewnętrzny |
|
| InvalidRequest |
|
| ItemValueSizeTooLarge |
|
Testowanie i debugowanie w Studio
Dane w MemoryStoreService są izolowane między Studio a produkcją, więc zmiana danych w Studio nie wpływa na zachowanie produkcji. Oznacza to, że Twoje wywołania API z Studio nie mają dostępu do danych produkcyjnych, co pozwala na bezpieczne testowanie pamięci przechowywania i nowych funkcji przed wdrożeniem do produkcji.
Testowanie w Studio ma te same limity i kwoty co produkcja. W przypadku kwot obliczanych na podstawie liczby użytkowników, wynikowa kwota może być bardzo mała, ponieważ jesteś jedynym użytkownikiem podczas testów w Studio. Podczas testowania w Studio możesz także zauważyć nieco wyższe opóźnienie i zwiększone wskaźniki błędów w porównaniu do użytkowania w produkcji z powodu dodatkowych kontroli, które są przeprowadzane, aby zweryfikować dostęp i uprawnienia.
Aby uzyskać informacje na temat tego, jak debugować pamięć przechowywania w grach na żywo lub podczas testowania w studio, użyj Konsoli dewelopera.