Użyj tych praktyk, aby zorganizować tymczasowe dane, rozłożyć obciążenie i reagować na problemy z pamięcią podręczną.
W zależności od typu struktury danych, MemoryStoreService egzekwuje limity dotyczące pamięci i liczby elementów w strukturze danych. Wszystkie struktury danych są również ograniczone przez globalny limit żądań na partycję.
Projektowanie kluczy i struktur danych
Zobacz Użyj statycznych wzorców kluczy i prefiksów w Najlepszych praktykach dotyczących pamięci podręcznych. Zastosuj te wzorce zarówno do kluczy, jak i nazw struktur danych, aby każdy serwer kierował te same logiczne dane do tej samej lokalizacji.
Wybierz czasy wygaśnięcia, które odpowiadają temu, jak długo dane pozostają użyteczne. Nie używaj pamięci podręcznej do przechowywania trwałych rekordów graczy lub danych, które muszą przetrwać wygaśnięcie. Aby uzyskać pomoc w wyborze między usługami, zobacz Pamięci podręczne a pamięci podręczne.
Obsługa niepowodzeń żądań
Zobacz Ponów próby w przypadku niepowodzeń przejściowych w Najlepszych praktykach dotyczących pamięci podręcznych.
Preferuj UpdateAsync zamiast SetAsync
Zobacz Preferuj UpdateAsync zamiast SetAsync w Najlepszych praktykach dotyczących pamięci podręcznych. Mapy haszowe i mapy posortowane oferują MemoryStoreHashMap:UpdateAsync() i MemoryStoreSortedMap:UpdateAsync() dla tego wzorca.
Rozkładaj powtarzające się żądania
Zobacz Rozkładaj powtarzające się żądania w Najlepszych praktykach dotyczących pamięci podręcznych.
Monitoruj użycie
Użyj Dashboardu Obserwowalności Pamięci Podręcznej, aby monitorować wykorzystanie kwot, wolumen żądań i statusy odpowiedzi. Przejrzyj wbudowane powiadomienia e-mail, skonfiguruj niestandardowe powiadomienia dla ważnych metryk pamięci podręcznej i użyj Raportu o błędach, aby zbadać niepowodzenia.
Zredukuj żądania, rozmiary elementów i czasy wygaśnięcia przed zwiększeniem pojemności. Jeśli uzasadnione użycie przekracza domyślne kwoty, oceń Rozszerzone Usługi.
Zarządzaj limitami mapy posortowanej i kolejki
Mapy posortowane i kolejki mają limity dotyczące maksymalnej liczby elementów i maksymalnej całkowitej pamięci. Dodatkowo, elementy w jednej z tych struktur danych zawsze znajdują się na jednej partycji. Każde żądanie do jednej z tych struktur danych jest żądaniem do tej samej partycji.
Gdy mapa posortowana lub kolejka osiągnie swój limit elementów lub pamięci, usuń niepotrzebne elementy ręcznie lub dodaj politykę wygaśnięcia. Jeśli tylko limit pamięci powoduje ograniczenia, zmniejsz rozmiary elementów, usuwając niepotrzebne informacje z kluczy i wartości.
Jeśli potrzebujesz wszystkich swoich elementów lub doświadczasz ograniczeń z powodu przepustowości żądań, jedynym rozwiązaniem jest sharding.
Rozkładaj obciążenie za pomocą sharding
Sharding to proces przechowywania zestawu powiązanych danych w wielu strukturach danych. Innymi słowy, oznacza to zastąpienie istniejącej, o wysokiej przepustowości struktury danych wieloma, mniejszymi, które razem zawierają ten sam zestaw danych co oryginał.
Kluczowym wyzwaniem w sharding jest znalezienie sposobu na rozłożenie danych w wielu strukturach danych w sposób, który zachowuje tę samą funkcjonalność co oryginał.
Chociaż Roblox już partycjonuje mapy haszowe, możesz dodatkowo je shardować, rozkładając żądania między kilka kluczy.
Sharding mapy posortowanej
Aby shardować rekordy graczy w mapie posortowanej, użyj arytmetyki modulo, aby przypisać każdy Id do jednej z ustalonej liczby map. Poniższy przykład używa czterech map i konsekwentnie kieruje tego samego użytkownika do tej samej mapy:
-- Inicjalizuj usługę pamięci podręcznej
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Utwórz swoje wiadra mapy posortowanej
local sm1 = MemoryStoreService:GetSortedMap("sm1")
local sm2 = MemoryStoreService:GetSortedMap("sm2")
local sm3 = MemoryStoreService:GetSortedMap("sm3")
local sm4 = MemoryStoreService:GetSortedMap("sm4")
local sortedMaps = { sm1, sm2, sm3, sm4 }
-- Funkcja pomocnicza do pobierania odpowiedniego wiadra z klucza elementu
local function getSortedMapBucket(userId)
local bucketIndex = (userId % #sortedMaps) + 1
return sortedMaps[bucketIndex]
end
-- Inicjalizuj graczy z domyślną wartością 0
for _, player in game:GetService("Players"):GetPlayers() do
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
bucket:SetAsync(tostring(userId), 0, 600)
end
-- Pobierz wartość gracza
local player = game:GetService("Players"):GetPlayers()[1]
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
local playerScore = bucket:GetAsync(tostring(userId))
print(playerScore)Sharding kolejki
Shardowanie kolejki jest trudniejsze niż shardowanie mapy posortowanej. Chociaż chcesz rozłożyć przepustowość żądań na wiele kolejek, dodania, odczyty i usunięcia występują tylko na początku lub końcu kolejki.
Jednym z rozwiązań jest użycie obracającej się kolejki, co oznacza stworzenie wielu kolejek i rotację między nimi, gdy dodajesz lub odczytujesz element:
- Utwórz kilka kolejek i dodaj je do tablicy.
- Utwórz dwa lokalne wskaźniki. Jeden reprezentuje kolejkę, z której chcesz odczytać i usunąć elementy. Drugi reprezentuje kolejkę, do której chcesz dodać elementy:
- Dla operacji odczytu oblicz liczbę elementów, które potrzebujesz z każdej kolejki, a także gdzie przenieść wskaźnik odczytu.
- Dla operacji usunięcia przekaż identyfikatory z odczytu do każdej kolejki.
- Dla operacji dodawania dodaj do kolejki na wskaźniku dodawania i zwiększ wskaźnik.
-- Inicjalizuj usługę pamięci podręcznej
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Utwórz swoje kolejki
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- Umieść kolejki w tablicy
local queueArr = { q1, q2, q3, q4 }
-- Utwórz dwa wskaźniki reprezentujące indeksy kolejek odczytu i dodawania
local readIndex = 1
local addIndex = 1
-- Utwórz lokalną funkcję, która odpowiednio aktualizuje indeksy
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Utwórz lokalną funkcję, która odczytuje n elementów z kolejki
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- pętla przez każdą kolejkę
for i = 1, 4, 1 do
-- określ, czy ta kolejka odczyta dodatkowy element
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- odczytuj elementy z każdej kolejki
-- +1 elementy, jeśli spełnia dodatkowe kryteria odczytu
if diff < endIndex then
items[i], ids[i] = queue:ReadAsync(countPerQueue + 1, allOrNothing, waitTimeout)
else
items[i], ids[i] = queue:ReadAsync(countPerQueue, allOrNothing, waitTimeout)
end
end
readIndex = rotateIndex(readIndex, count)
return items, ids
end
-- Utwórz lokalną funkcję, która usuwa n elementów z kolejki
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[i]
queue:RemoveAsync(ids[i])
end
end
-- Utwórz lokalną funkcję, która dodaje element do kolejki
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[addIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Napisz trochę kodu!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player.User.Id, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)Mapy haszowe
Mapy haszowe nie mają indywidualnych limitów pamięci ani liczby elementów i są automatycznie shardowane, ale nadal możesz napotkać ograniczenia, jeśli używasz ich źle.
Na przykład, rozważ grę z mapą haszową danych, przechowywaną jako wartość pojedynczego klucza o nazwie metadata. Jeśli te metadane zawierają zagnieżdżony obiekt z informacjami takimi jak identyfikator miejsca, liczba graczy i inne, za każdym razem, gdy potrzebujesz metadanych, nie masz innego wyboru, jak tylko wywołać GetAsync("metadata") i pobrać cały obiekt. W takim przypadku wszystkie żądania trafiają do jednego klucza, a zatem do jednej partycji.
Zamiast przechowywać wszystkie metadane jako jeden, zagnieżdżony obiekt, przechowuj każde niezależnie dostępne pole jako swój własny klucz, aby mapa haszowa mogła skorzystać z automatycznego sharding. Jeśli potrzebujesz separacji między metadanymi a resztą mapy haszowej, dodaj prefiks nazwy, taki jak metadata_user_count zamiast user_count.
Jeśli jeden lub kilka kluczy otrzymuje częste żądania, sharduj te wywołania między wieloma kluczami. Na przykład, jeśli wszystkie serwery gry pobierają wartość z jednego klucza mapy haszowej, żądania mogą powodować ograniczenia partycji. Aby zmniejszyć obciążenie, skopiuj wartość do wielu kluczy i kieruj każdy serwer do stabilnego shardu.