MemoryStoreService to usługa danych o wysokiej przepustowości i niskim opóźnieniu, która zapewnia szybkie przechowywanie danych w pamięci dostępne ze wszystkich serwerów w aktywnej sesji. Pamięciowe magazyny są odpowiednie dla częstych i efemerycznych danych, które szybko się zmieniają i nie muszą być trwałe, ponieważ są szybsze w dostępie i znikają po osiągnięciu maksymalnego czasu życia. W przypadku danych, które muszą być trwałe między sesjami, użyj magazynów danych.
Struktury danych
Zamiast bezpośrednio uzyskiwać dostęp do surowych danych, pamięciowe magazyny mają trzy podstawowe struktury danych współdzielone między serwerami w celu szybkiego przetwarzania: posortowana mapa, kolejka i mapa haszująca. Każda struktura danych jest dobrze dopasowana do określonych przypadków użycia:
- Dopasowywanie oparte na umiejętnościach - Zapisz informacje o użytkownikach, takie jak poziom umiejętności, w współdzielonej kolejce między serwerami i użyj serwerów lobby do okresowego przeprowadzania dopasowań.
- Handel i aukcje między serwerami - Umożliw uniwersalny handel między różnymi serwerami, gdzie użytkownicy mogą licytować przedmioty z cenami zmieniającymi się w czasie rzeczywistym, przy użyciu posortowanej mapy par klucz-wartość.
- Globalne rankingi - Przechowuj i aktualizuj rankingi użytkowników na współdzielonej tablicy wyników w posortowanej mapie.
- Wspólne inwentarze - Zapisz przedmioty inwentarza i statystyki w współdzielonej mapie haszującej, gdzie użytkownicy mogą jednocześnie korzystać z przedmiotów inwentarza.
- Cache dla danych trwałych - Synchronizuj i kopiuj swoje trwałe dane w magazynie danych do pamięciowej mapy haszującej, która może działać jako cache i poprawić wydajność twojej gry.
Ogólnie, jeśli potrzebujesz uzyskać dostęp do danych na podstawie konkretnego klucza, użyj mapy haszującej. Jeśli potrzebujesz, aby te dane były uporządkowane, użyj posortowanej mapy. Jeśli potrzebujesz przetwarzać swoje dane w określonej kolejności, użyj kolejki.
Limity i kwoty
Aby utrzymać skalowalność i wydajność systemu, pamięciowe magazyny mają kwoty użycia danych dla rozmiaru pamięci, żądań API i rozmiaru struktury danych.
Pamięciowe magazyny mają politykę usuwania opartą na czasie wygaśnięcia, znaną również jako czas życia (TTL). Przedmioty są usuwane po wygaśnięciu, a kwota pamięci jest zwalniana dla nowych wpisów. Gdy osiągniesz limit pamięci, żądania, które zwiększają użycie pamięci, nie powiodą się, dopóki nie zwolnisz pamięci lub nie zwiększysz swojej kwoty. Zobacz Odzyskiwanie z wyczerpania kwoty pamięci w celu uzyskania opcji.
Kwota rozmiaru pamięci
Kwota pamięci ogranicza całkowitą ilość pamięci, którą gra może zużywać. To nie jest stała wartość; zamiast tego zmienia się w czasie w zależności od liczby użytkowników w grze zgodnie z formułą 64 KB + 1.2 KB * [liczba użytkowników]. Kwota dotyczy poziomu gry, a nie poziomu serwera.
Gdy 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 śledzenia wynoszący osiem dni, zanim kwota zostanie ponownie oceniona na niższą wartość.
Po osiągnięciu limitu rozmiaru pamięci w twojej grze, wszelkie żądania API, które zwiększają rozmiar pamięci, zawsze się nie powiodą. Żądania, które zmniejszają lub nie zmieniają rozmiaru pamięci, nadal będą się powodzić.
Dzięki pulpitowi obserwowalności możesz w czasie rzeczywistym zobaczyć kwotę rozmiaru pamięci swojej gry za pomocą wykresu Użycie pamięci.
Limity żądań API
Kwota jednostek żądań dotyczy 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, podobnie jak MemoryStoreSortedMap:GetRangeAsync(), ale zużywa dodatkową jednostkę co dwie 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 alokacji żądań między serwerami, o ile całkowita szybkość żądań nie przekracza kwoty. Jeśli przekroczysz kwotę, otrzymasz odpowiedź o błędzie, gdy usługa ograniczy twoje żądania.
Dzięki funkcji obserwowalności możesz w czasie rzeczywistym zobaczyć kwotę jednostek żądań 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 partycję
Oprócz kwoty jednostek żądań na poziomie gry, pamięciowe magazyny stosują limity żądań do każdej partycji jako zabezpieczenie, które chroni stabilność usługi dla wszystkich gier. Te limity nie są kwotą, która jest przydzielana twojej grze, i nie są twardym sufitem dla przepustowości, którą twoja gra może osiągnąć. Poniższe wartości są szacunkowe. Są skonfigurowane w backendzie Roblox i mogą się zmieniać, więc nie projektuj ich jako stałych liczb.
Każde żądanie liczy się do limitu partycji, która przechowuje przedmiot, niezależnie od struktury danych. Możesz obecnie oczekiwać, że ograniczenia zaczną się przy około 30,000 jednostek żądań na minutę dla pojedynczej partycji, więc staraj się pozostać znacznie poniżej tej wartości. To, ile z tego limitu zużywa struktura danych, zależy od tego, jak jej przedmioty są rozłożone:
- Posortowane mapy i kolejki znajdują się na jednej partycji, więc każde żądanie do jednej z tych struktur danych liczy się do tego samego limitu partycji.
- Mapy haszujące rozprzestrzeniają swoje przedmioty na wiele partycji, więc ruch rozłożony na wiele kluczy przedmiotów rzadko zbliża się do limitu jakiejkolwiek jednej partycji.
Mapy haszujące mają dodatkowy limit na każdy klucz przedmiotu wynoszący około 5,000 jednostek żądań zapisu na minutę i 15,000 jednostek żądań odczytu na minutę. Ten limit na klucz dotyczy ponad limitu na partycję, więc często używany klucz może trafić na którykolwiek z nich. Limit na klucz dotyczy tylko operacji na pojedynczym kluczu: limit odczytu dotyczy MemoryStoreHashMap:GetAsync(), limit zapisu dotyczy MemoryStoreHashMap:SetAsync() i MemoryStoreHashMap:RemoveAsync(), a MemoryStoreHashMap:UpdateAsync() liczy się zarówno do limitów odczytu, jak i zapisu. MemoryStoreHashMap:ListItemsAsync() skanuje partycje, a nie pojedynczy klucz, więc tylko limit na partycję ma do niego zastosowanie.

Jeśli nie potrzebujesz sortowania ani funkcjonalności FIFO, mapa haszująca jest zazwyczaj najlepszym wyborem, ponieważ może rozłożyć obciążenie na partycje.
Gdy żądania przekraczają limit partycji lub limit na klucz, usługa je ogranicza i zwraca kod statusu PartitionRequestsOverLimit, który możesz monitorować za pomocą pulpitu obserwowalności.
Jeśli spodziewasz się, że struktura danych lub klucz przedmiotu otrzyma wysoką, stałą szybkość żądań, podziel ją, aby obciążenie rozłożyło się na więcej partycji lub kluczy. Rozłóż odczyty i zapisy równomiernie na wiele kluczy przedmiotów, aby pozostać w granicach limitów na klucz. Możesz również zmniejszyć szybkość żądań, buforując wartości na serwerze i ponownie sprawdzając je po pewnym czasie, grupując żądania tam, gdzie to możliwe, oraz stosując wykładnicze opóźnienie, gdy otrzymasz odpowiedzi o ograniczeniu.
Odzyskiwanie z wyczerpania kwoty pamięci
Jeśli twoja gra otrzymuje błędy TotalMemoryOverLimit, sprawdź wykres Użycie pamięci w pulpicie obserwowalności. Aby wznowić zapisy, które zwiększają użycie pamięci, usuń niepotrzebne przedmioty, poczekaj na wygaśnięcie przedmiotów lub skorzystaj z jednej z poniższych opcji.
Opróżnij pamięciowe magazyny za pomocą Open Cloud
Jeśli nie potrzebujesz już przechowywanych danych, użyj punktu końcowego Open Cloud Flush Memory Store, aby asynchronicznie wyczyścić wszystkie struktury danych pamięciowych w uniwersum dla wybranego zakresu.
Aby opróżnić pamięciowe magazyny:
- Utwórz klucz API z uprawnieniami do opróżniania pamięciowych magazynów i uzyskiwania statusu operacji opróżniania dla docelowego uniwersum.
- Wyślij żądanie POST do https://apis.roblox.com/cloud/v2/universes/{universe_id}/memory-store:flush z kluczem API w nagłówku x-api-key. Zastąp {universe_id} identyfikatorem uniwersum twojej gry. Ustaw parametr zapytania scope na LIVE dla danych produkcyjnych lub TEST dla danych Studio. Jeśli pominiesz scope, punkt końcowy użyje LIVE.
- Użyj Get Memory Store Flush Operation, aby sprawdzić operację zwróconą przez żądanie opróżnienia, aż done będzie true, i sprawdź, czy wystąpił error. Użyj tego samego zakresu podczas sprawdzania. Zwrócona path już zawiera cloud/v2, więc sprawdzaj pod https://apis.roblox.com/{path} bez dodawania dodatkowego prefiksu.
Pamięciowe magazyny Studio i produkcji są izolowane. Opróżnienie jednego zakresu nie czyści drugiego. Po usunięciu niepotrzebnych danych, postępuj zgodnie z najlepszymi praktykami, takimi jak krótsze czasy wygaśnięcia i jawne usuwanie, aby zapobiec ponownemu osiągnięciu limitu użycia pamięci.
Zwiększ pojemność za pomocą Rozszerzonych Usług
Jeśli twoja gra musi zachować dane i używać więcej pamięci, niż pozwala na to domyślna kwota, włącz Rozszerzone Usługi dla pamięciowych magazynów. Rozszerzone Usługi pozwalają płacić za przechowywanie pamięciowych magazynów i żądania przekraczające domyślne kwoty.
Aby rozpocząć, spełnij wymagania kwalifikacyjne, dodaj dane konta i metodę płatności, a następnie wybierz swoją grę w Pulpicie Twórcy. W sekcji Rozszerzone Usługi włącz pamięciowe magazyny i ustaw miesięczne budżety na potrzebne zasoby. Zobacz Włącz usługę i Cennik usług w celu uzyskania szczegółów.
Rozszerzone Usługi ograniczają usługę, jeśli użycie przekracza twój budżet. Monitoruj użycie i rozliczenia, a także kontynuuj usuwanie niepotrzebnych danych.
Najlepsze praktyki
Aby utrzymać optymalny wzór użycia pamięci i uniknąć osiągnięcia limitów, postępuj zgodnie z tymi najlepszymi praktykami:
Usuwaj przetworzone przedmioty. Regularne czyszczenie odczytanych przedmiotów za pomocą metody MemoryStoreQueue:RemoveAsync() dla kolejek i MemoryStoreSortedMap:RemoveAsync() dla posortowanych map może zwolnić pamięć i utrzymać strukturę danych na bieżąco.
Ustaw czas wygaśnięcia na jak najkrótszy czas podczas dodawania danych. Chociaż domyślny czas wygaśnięcia wynosi 45 dni zarówno dla MemoryStoreQueue:AddAsync(), jak i MemoryStoreSortedMap:SetAsync(), ustawienie najkrótszego możliwego czasu może automatycznie oczyścić stare dane, aby zapobiec ich zapełnieniu twojej kwoty użycia pamięci.
- Nie przechowuj dużej ilości danych z długim czasem wygaśnięcia, ponieważ ryzykujesz przekroczenie kwoty pamięci i potencjalnie spowodowanie problemów, które mogą zniszczyć całą twoją grę.
- Zawsze albo jawnie usuwaj niepotrzebne przedmioty, albo ustaw krótki czas wygaśnięcia przedmiotu.
- Ogólnie powinieneś używać jawnego usuwania do zwalniania pamięci, a wygaśnięcia przedmiotów jako mechanizmu zabezpieczającego, aby zapobiec zajmowaniu pamięci przez nieużywane przedmioty przez dłuższy czas.
Przechowuj w pamięci tylko niezbędne wartości.
Na przykład, w grze o aukcjach, musisz tylko utrzymać najwyższą ofertę. Możesz użyć MemoryStoreSortedMap:UpdateAsync() na jednym kluczu, aby utrzymać najwyższą ofertę, zamiast przechowywać wszystkie oferty w swojej strukturze danych.
Użyj wykładniczego 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, potem czterech, ośmiu itd., zamiast ciągle wysyłać żądania do MemoryStoreService, aby uzyskać poprawną odpowiedź.
Podziel ogromne struktury danych na mniejsze, korzystając z shardingu.
Często łatwiej jest zarządzać danymi w mniejszych strukturach, niż przechowywać wszystko w jednej dużej strukturze danych. To podejście może również pomóc uniknąć limitów użycia 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. W przypadku szczególnie popularnej gry możesz nawet podzielić użytkowników na wiele map w oparciu o ostatnie cyfry ich identyfikatorów użytkowników.
Shard często używane klucze w mapach haszujących z wieloma kopiami klucza, aby rozłożyć obciążenie.
Kompresuj przechowywane wartości.
Na przykład, rozważ użycie algorytmu LZW, aby zmniejszyć rozmiar przechowywanej wartości.
Zapisz się do Rozszerzonych Usług.
W przypadku przechowywania lub użycia żądań przekraczających domyślne kwoty, włącz Rozszerzone Usługi dla pamięciowych magazynów.
Obserwowalność
Pulpit Obserwowalności zapewnia wgląd i analizy do monitorowania i rozwiązywania problemów z użyciem pamięciowych magazynów. Dzięki wykresom aktualizowanym w czasie rzeczywistym na różnych aspektach użycia pamięci i żądań API możesz śledzić wzór użycia pamięci swojej gry, zobaczyć aktualnie przydzielone kwoty, monitorować status API i identyfikować potencjalne problemy w celu optymalizacji wydajności.
Poniższa tabela wymienia i opisuje wszystkie kody statusu odpowiedzi API dostępne na wykresach Liczba żądań według statusu i Żądania według API x status na Pulpicie Obserwowalności. Aby uzyskać więcej informacji na temat rozwiązywania tych błędów, zobacz Rozwiązywanie problemów. Aby uzyskać szczególną kwotę lub limit, do którego odnosi się błąd, zobacz Limity i kwoty.
| Kod statusu | Opis |
|---|---|
| Sukces | Sukces. |
| DataStructureMemoryOverLimit | Przekracza limit rozmiaru pamięci na poziomie struktury danych (100 MB). |
| DataUpdateConflict | Konflikt z powodu równoczesnej aktualizacji. |
| AccessDenied | Nieautoryzowany dostęp 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ę lub na klucz. |
| TotalRequestsOverLimit | Przekracza limit jednostek żądań na poziomie uniwersum. |
| TotalMemoryOverLimit | Przekracza limit kwoty pamięci na poziomie uniwersum. |
| ItemValueSizeTooLarge | Rozmiar wartości przekracza limit (32 KB). |
Poniższa tabela wymienia kody statusu z strony klienta, które obecnie nie są dostępne na Pulpicie Obserwowalności.
| Kod statusu | Opis |
|---|---|
| InternalError | Błąd wewnętrzny. |
| UnpublishedPlace | Musisz opublikować to miejsce, aby używać MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService musi być wywoływane z serwera. |
| InvalidExpirationTime | Pole 'expiration' 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 prawidłową liczbę lub ciąg. |
| TransformCallbackFailed | Nie udało się wywołać funkcji zwrotnej transformacji. |
| RequestThrottled | Ostatnie żądania MemoryStores osiągnęły jeden lub więcej limitów. |
| UpdateConflict | Przekroczono maksymalną liczbę prób. |
Rozwiązywanie problemów
Poniższa tabela wymienia i opisuje zalecaną metodę rozwiązania dla każdego kodu statusu odpowiedzi:
| Błąd | Opcje rozwiązywania problemów |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit |
|
| DataUpdateConflict |
Przykład przerywania żądania |
| 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 uzyskują dostępu do danych produkcyjnych, co pozwala na bezpieczne testowanie pamięciowych magazynów i nowych funkcji przed przejściem 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 testowania w Studio. Podczas testowania z poziomu Studio możesz również zauważyć nieco wyższe opóźnienia i podwyższone wskaźniki błędów w porównaniu do użycia w produkcji z powodu dodatkowych kontroli, które są wykonywane w celu weryfikacji dostępu i uprawnień.
Aby uzyskać informacje na temat debugowania pamięciowego magazynu w grach na żywo lub podczas testowania w studio, skorzystaj z Konsoli Dewelopera.