W zależności od typu struktury danych, MemoryStoreService narzuca limity na pamięć i liczbę elementów w strukturze danych. Wszystkie struktury danych są również ograniczone przez globalny limit żądań na partycję.
Każda gra Roblox ma Pulpit Obserwacji Pamięci, który zawiera szereg wykresów, których możesz użyć do monitorowania wykorzystania pamięci.
Posortowane mapy i kolejki
Posortowane mapy i kolejki mają ograniczenia 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 posortowana mapa lub kolejka osiągnie swój limit elementów lub pamięci, najlepszym rozwiązaniem jest ręczne usunięcie zbędnych elementów lub dodanie polityki wygasania dla elementów. Alternatywnie, jeśli tylko limit pamięci powoduje throttling, możesz spróbować zredukować rozmiar swoich elementów, eliminując zbędne informacje z kluczy i wartości.
Jeśli potrzebujesz wszystkich swoich elementów lub doświadczasz throttlingu z powodu przepustowości żądań, jedynym rozwiązaniem jest sharding.
Sharding
Sharding to proces przechowywania zestawu powiązanych danych w różnych strukturach danych. Innymi słowy, oznacza to zastąpienie istniejącej, wysokoprzepustowej struktury danych wieloma, mniejszymi, które razem zawierają ten sam zestaw danych co oryginalna.
Kluczowym wyzwaniem przy shardingu jest znalezienie sposobu na rozłożenie danych w różnych strukturach danych w sposób, który zachowuje tę samą funkcjonalność co oryginalna.
W przypadku map haszowych, chociaż struktura danych jest już podzielona, sharding polega na rozkładaniu żądań między różne klucze.
Sharding posortowanej mapy
Aby shardować posortowaną mapę, rozważ podzielenie danych na alfabetyczne podsekcje z zakresami znaków. Na przykład, zakładając, że masz tylko klucze z pierwszym literą od A do Z, a uważasz, że cztery posortowane mapy są wystarczające dla twojego obecnego przypadku użycia i przyszłego wzrostu:
- Pierwsza mapa może obejmować A-G, druga H-N, trzecia O-T, a czwarta U-Z.
- Aby wstawić lub pobrać element, użyj odpowiedniej mapy w zależności od początkowego znaku elementu.
Sharding posortowanej mapy
-- Inicjalizacja usługi MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Utwórz swoje kubełki mapy posortowanej
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- Funkcja pomocnicza do pobierania właściwego kubełka z klucza elementu
local function getSortedMapBucket(itemKey)
if (itemKey >= "a" and itemKey < "h") then
return sm_AtoG
elseif (itemKey < "n") then
return sm_HtoM
elseif (itemKey < "u") then
return sm_NtoT
else
return sm_UtoZ
end
end
-- Inicjalizowanie nazw graczy z domyślną wartością 0
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Pobierz wartość gracza
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)
Sharding kolejki
Sharding kolejki jest trudniejszy niż sharding posortowanej mapy. Chociaż chcesz rozłożyć przepustowość żądań na kilka kolejek, dodawanie, odczytywanie i usuwanie odbywa się tylko z przodu lub tyłu kolejki.
Jednym z rozwiązań jest użycie obracającej się kolejki, co oznacza tworzenie kilku kolejek i rotowanie między nimi podczas dodawania lub odczytywania elementu:
Utwórz kilka kolejek i dodaj je do tablicy.
Utwórz dwa wskaźniki lokalne. 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, oraz gdzie przesunąć wskaźnik odczytu.
- Dla operacji usuwania przekaż identyfikatory z odczytu do każdej kolejki.
- Dla operacji dodawania dodawaj do kolejki na wskaźniku dodawania i zwiększaj wskaźnik.
Sharding kolejki
-- Inicjalizacja usługi MemoryStore
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 do odczytu i dodawania
local readIndex = 1
local addIndex = 1
-- Utwórz funkcję lokalną, która odpowiednio aktualizuje indeksy
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Utwórz funkcję lokalną, 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 = {}
-- przejdź 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]
-- odczytaj elementy z każdej kolejki
-- +1 elementy, jeśli spełniają kryteria dodatkowego 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 funkcję lokalną, która usuwa n elementów z kolejki
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- Utwórz funkcję lokalną, która dodaje element do kolejki
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Napisz trochę kodu!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 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ć throttling, jeśli użyjesz 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 tym przypadku wszystkie żądania chodzą do jednego klucza, a zatem do jednej partycji.
Zamiast przechowywać wszystkie metadane jako pojedynczy, zagnieżdżony obiekt, lepszym podejściem jest przechowywanie każdego pola jako własnego klucza, aby mapa haszowa mogła wykorzystać automatyczny sharding. Jeśli potrzebujesz oddzielić metadane od reszty mapy haszowej, dodaj prefiks nazewniczy (np. metadata_user_count zamiast po prostu user_count).
Dodatkowo, jeśli jeden lub kilka kluczy jest często wywoływanych, ważne jest, aby shardować te wywołania pośród wielu kluczy. Na przykład, jeśli wszystkie serwery gry muszą pobrać wartość z jednego klucza mapy haszowej, możesz napotkać throttling partycji. Aby temu zapobiec, możesz rozłożyć te wywołania pośród kilku kluczy z tą samą wartością, aż throttling partycji przestanie występować.