Migliori Pratiche quando si progettano Strutture dei Dati del MemoryStore

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

A seconda del inserisci / scrividi struttura dei dati, MemoryStoreService impone 限i sulla memoria e sul numero di oggetti in una struttura dei dati. Tutte le strutture dei dati sono inoltre limitate da un limite globale per la partizione.

Ogni esperienza Roblox ha il Memory Store Observability Dashboard, che include un insieme di grafici che puoi utilizzare per monitorare l'utilizzo della memoria.

Mappe e code sortite

Le mappe e le code sono entrambe limitate dal numero massimo di oggetti e dalla memoria massima. Inoltre, gli elementi in una di queste strutture di dati sono sempre presenti in una sola partizione. Ogni richiesta a una di queste strutture di dati è una richiesta alla stessa partizione.

Quando una mappa o una coda排序 raggiunge il suo item o limite di memoria, il miglior corso d'azione è rimuovere gli elementi non necessari manualmente o aggiungendo una politica di scadenza per gli elementi. In alternativa, se solo il limite di memoria sta causando un throttling, puoi tentare di ridurre la dimensione dei tuoi elementi rimuovendo le informazioni non necessarie dai tuoi chiavi e valori.

Se hai tutti i tuoi oggetti o stai riscontrando un aumento delle richieste, l'unica soluzione è sharding.

Dividere

Lo sharding è il processo di memorizzazione di un insieme di dati relativi in più strutture di dati. In altre parole, significa prendere una struttura di dati esistente e sostituirla con più strutture di dati più piccole che insieme contengono lo stesso insieme di dati dell'originale.

La sfida chiave per lo sharding è trovare un modo per distribuire i dati su più strutture di dati in un modo che mantenga le stesse funzionalità dell'originale.

Dividere una mappa ordinata

Per schiacciare una mappa ordinata, considera dividere i tuoi dati in sottotesti alfabetici con gamma di caratteri. Ad esempio, supponi che tu abbia solo chiavi con la prima lettera da A-Z e che tu creda che quattro mappe ordinati siano sufficienti per il tuo caso d'uso attuale e la tua 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 Articolo, usa il mappeggiatore appropriato in base al personaggio di partenza dell'Articolo.
Dividere una mappa ordinata

-- Inizializza il servizio MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea i tuoi secchielli mappa ordinati
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 salvataggio per recuperare il contenitore 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 il 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)

Dividere una coda

Dividere una coda è più complicato di dividere una mappa ordinata. Anche se vuoi distribuire il flusso di richieste su più cune, aggiungi, leggi e rimuovi solo quando si verifica la coda frontale o posteriore.

Una soluzione è utilizzare una coda girevole, che significa creare più cose da fare e girare tra loro quando aggiungi o leggi un Articolo:

  1. Crea più coda e aggiungili a un vettore.

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

    • Per le operazioni di lettura, calcola il numero di oggetti che hai bisogno di ciascuna coda, nonché dove spostare il punto di lettura.
    • Per le operazioni di rimozione, passa gli ID dalla lettura a ciascuna coda.
    • Per le operazioni di aggiungimento, aggiungi alla coda al punto di aggiungimento e incrementa il punto di aggiungimento.
Dividere 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 punteri che rappresentano gli indici della lettura e aggiungi code di attesa
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 oggetti dalla coda
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- iter attraverso ciascuna coda
for i = 1, 4, 1 do
-- determina se questa coda leggerà un articoloextra
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- leggi gli elementi da ciascuna coda
-- +1 oggetti se soddisfa i criteri di lettura extra
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 oggetti 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 oggetto alla coda
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Scrivi del codice!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)

Mappy di Hashes

Le mappe hash non hanno limiti di memoria o di oggetti individuali e vengono automaticamente distrutte, ma puoi ancora incontrare un aumento delle prestazioni se le usi male.

Ad esempio, considera un'esperienza con una mappa di dati di gioco, memorizzata come valore di una singola chiave chiamata metadata . Se questa metadaten contiene un oggetto in cui informazioni come il luogo ID, il numero di giocatori e altro sono memorizzati, ogni volta che la metadaten è necessaria, non hai scelta che chiamare GetAsync("metadata") e recuperare l'intero

Invece di memorizzare tutto il metadato come un singolo oggetto in più, l'approccio migliore è memorizzare ogni campo come sua propria chiave in modo che la mappa di hash possa beneficiare della frammentazione automatica. Se hai bisogno di separazione tra il metadato e il resto della mappa di hash, aggiungi un prefisso di nome (ad esempio, metadata_user_count invece di solo user_count .