Migliori pratiche nella progettazione delle strutture dati MemoryStore

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

A seconda del tipo di struttura dati, MemoryStoreService applica limiti sulla memoria e sul numero di elementi in una struttura dati. Tutte le strutture dati sono inoltre soggette a un limite globale di richiesta per partizione.

Ogni gioco Roblox ha il Dashboard di Osservabilità del Memory Store, che include un insieme di grafici che puoi utilizzare per monitorare l'uso del memory store.

Mappe ordinate e code

Le mappe ordinate e le code hanno entrambi limiti sul numero massimo di elementi e sulla memoria totale massima. Inoltre, gli elementi in una di queste strutture dati risiedono sempre su una singola partizione. Ogni richiesta a una di queste strutture dati è una richiesta alla stessa partizione.

Quando una mappa ordinata o una coda raggiunge il suo limite di elementi o di memoria, la migliore linea d'azione è rimuovere manualmente gli elementi non necessari o aggiungere una politica di scadenza per gli elementi. In alternativa, se solo il limite di memoria causa il throttling, puoi provare a ridurre le dimensioni dei tuoi elementi rimuovendo informazioni non necessarie dalle tue chiavi e valori.

Se hai bisogno di tutti i tuoi elementi o stai sperimentando il throttling a causa del throughput delle richieste, l'unica soluzione è lo sharding.

Sharding

Lo sharding è il processo di memorizzare un insieme di dati correlati su più strutture dati. In altre parole, significa prendere una struttura dati esistente ad alta capacità di richiesta e sostituirla con più strutture più piccole che insieme contengono lo stesso insieme di dati dell'originale.

La principale sfida dello sharding è trovare un modo per distribuire i dati su più strutture dati in modo da mantenere la stessa funzionalità dell'originale.

Per le mappe hash, sebbene la struttura dati sia già partizionata, lo sharding viene effettuato distribuendo le richieste tra diverse chiavi.

Sharding di una mappa ordinata

Per fare sharding di una mappa ordinata, considera di dividere i tuoi dati in sottosezioni alfabetiche con intervalli di caratteri. Ad esempio, supponi di avere solo chiavi con la prima lettera da A-Z, e credi che quattro mappe ordinate siano sufficienti per il tuo caso d'uso attuale e per la crescita futura:

  • La prima mappa può coprire A-G, la seconda H-N, la terza O-T e la quarta U-Z.
  • Per inserire o recuperare un elemento, utilizza la mappa appropriata in base al carattere iniziale dell'elemento.
Sharding di una Mappa Ordinata

-- Inizializza il servizio MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea i tuoi bucket di Mappa Ordinata
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- Funzione di supporto per recuperare il bucket corretto dalla chiave dell'elemento
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
-- Inizializza i nomi dei giocatori con valore predefinito di 0
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Recupera il valore di un giocatore
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)

Sharding di una coda

Lo sharding di una coda è più complicato rispetto allo sharding di una mappa ordinata. Sebbene tu voglia distribuire il throughput delle richieste su più code, le aggiunte, le letture e le rimozioni avvengono solo sempre all'inizio o alla fine della coda.

Una soluzione è utilizzare una coda rotante, il che significa creare più code e ruotare tra di esse quando aggiungi o leggi un elemento:

  1. Crea diverse code e aggiungile a un array.

  2. Crea due puntatori locali. Uno rappresenta la coda da cui vuoi leggere e rimuovere gli elementi. L'altro rappresenta la coda a cui vuoi aggiungere elementi:

    • Per le operazioni di lettura, calcola il numero di elementi di cui hai bisogno da ciascuna coda, così come dove spostare il puntatore di lettura.
    • Per le operazioni di rimozione, passa gli ID dalla lettura a ciascuna coda.
    • Per le operazioni di aggiunta, aggiungi alla coda al puntatore di aggiunta e incrementa il puntatore.
Sharding di una Coda

-- Inizializza il servizio MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea le tue Code
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- Metti le Code in un Array
local queueArr = { q1, q2, q3, q4 }
-- Crea due puntatori che rappresentano gli indici delle code di lettura e aggiunta
local readIndex = 1
local addIndex = 1
-- Crea una funzione locale che aggiorna gli indici in modo appropriato
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Crea una funzione locale che legge n elementi dalla coda
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- ciclo attraverso ogni coda
for i = 1, 4, 1 do
-- determina se questa coda leggerà un elemento extra
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- leggi elementi da ogni coda
-- +1 elementi se corrisponde ai criteri di lettura aggiuntivi
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
-- Crea una funzione locale che rimuove n elementi dalla coda
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- Crea una funzione locale che aggiunge un elemento alla coda
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Scrivi un po' di codice!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)

Mappe hash

Le mappe hash non hanno limiti individuali di memoria o conteggio degli elementi e sono automaticamente shardate, ma puoi comunque incontrare il throttling se le usi in modo inadeguato.

Ad esempio, considera un gioco con una mappa hash di dati, memorizzati come valore di una singola chiave chiamata metadata. Se questo metadata contiene un oggetto annidato con informazioni come ID del luogo, conteggio dei giocatori e altro, ogni volta che il metadata è necessario, non hai altra scelta che chiamare GetAsync("metadata") e recuperare l'intero oggetto. In questo caso, tutte le richieste vanno a una singola chiave e quindi a una singola partizione.

Piuttosto che memorizzare tutto il metadata come un unico oggetto annidato, l'approccio migliore è memorizzare ciascun campo come la propria chiave in modo che la mappa hash possa sfruttare il partizionamento automatico. Se hai bisogno di separazione tra il metadata e il resto della mappa hash, aggiungi un prefisso ai nomi (ad esempio metadata_user_count invece di semplicemente user_count).

Inoltre, se una o poche chiavi vengono accessibili frequentemente, è importante shardare queste chiamate tra molte chiavi. Ad esempio, se tutti i server di gioco devono recuperare un valore da una chiave di una mappa hash, potresti incontrare il throttling della partizione. Per prevenire questo, puoi distribuire quelle chiamate tra diverse chiavi con lo stesso valore fino a quando il throttling della partizione non si verifica più.

© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.