Mejores Prácticas al Diseñar Estructuras de Datos de MemoryStore

Este contenido se traduce usando la IA (Beta) y puede contener errores. Para ver esta página en inglés, haz clic en aquí.

Dependiendo del introducirde estructura de datos, MemoryStoreService impone límites en el almacenamiento y el número de elementos en una estructura de datos. Todas las estructuras de datos también están restringidas por un límite global de solicitudes por partición.

Cada experiencia de Roblox tiene el Panel de Observabilidad de Tienda de Memoria , que incluye un conjunto de gráficos que puedes usar para monitorear el uso de la tienda de memoria.

Mapas y colas ordenados

Los mapas y colas ordenados tienen límites en el número máximo de elementos y el máximo de almacenamiento total. Además, los elementos en una de estas estructuras de datos siempre residen en una sola partición. Cada solicitud a una de esas estructuras de datos es una solicitud a la misma partición.

Cuando un mapa o colas ordenadas alcanzan su límite de artículos o de almacenamiento, lo mejor es eliminar los artículos innecesarios manualmente o agregando una política de expiración para los artículos. Alternativamente, si solo el límite de almacenamiento está causando throttling, puede intentar reducir el tamaño de sus artículos eliminando información innecesaria de sus teclas y valores.

Si necesita todos sus artículos o está experimentando una aceleración debido a la solicitud de rendimiento, la única solución es la fragmentación.

Fragmentación

La fragmentación es el proceso de almacenar un conjunto de datos relacionados en múltiples estructuras de datos. En otras palabras, significa tomar una estructura de datos existente de alto rendimiento y reemplazarla con múltiples estructuras más pequeñas que juntas contienen el mismo conjunto de datos que el original.

El desafío clave de la fragmentación es encontrar una manera de distribuir los datos a través de múltiples estructuras de datos de una manera que mantenga la misma funcionalidad que la original.

Fragmentando un Mapa Ordenado

Para fragmentar un mapa ordenado, considere dividir sus datos en subsecciones alfabéticas con rangos de caracteres. Por ejemplo, suponga que solo tiene llaves con la primera letra de A a Z, y cree que cuatro mapas ordenados son suficientes para su caso de uso actual y crecimiento futuro:

  • El primer mapa puede cubrir A-G, el segundo H-N, el tercer O-T y el cuarto U-Z.
  • Para insertar o recuperar un objeto, usa el mapa apropiado basado en el carácter inicial del objeto.
Fragmentando un Mapa Ordenado

-- Inicializar el Servicio de Almacenamiento de Memoria
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea tus cubos de mapas ordenados
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- Función de ayudante para recuperar el cubo correcto de la llave del artículo
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
-- Inicializa nombres de jugadores con un valor predeterminado de 0
for i,player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Recuperar el valor de un jugador
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)

Rompiendo una cola

Dividir una cola es más complicado que dividir un mapa ordenado. Aunque desee distribuir el rendimiento de la solicitud en múltiples colas, las adiciones, lecturas y eliminaciones solo ocurren en la parte delantera o trasera de la cola.

Una solución es usar una cola rotatoria, lo que significa crear múltiples colas y rotar entre ellas cuando agregues o leas un objeto:

  1. Crea varias colas y agrégalas a un matriz/lista.

  2. Cree dos punteros locales. Uno representa la cola de la que desea leer y eliminar elementos. El otro representa la cola a la que desea agregar elementos:

    • Para las operaciones de lectura, calcule el número de elementos que necesita de cada cola, así como a dónde mover el puntero de lectura.
    • Para eliminar operaciones, pase los ID de la lectura a cada cola.
    • Para agregar operaciones, agregue a la cola en el puntero de agregar e incremente el puntero.
Rompiendo una cola

-- Inicializar el Servicio de Almacenamiento de Memoria
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea tus colas
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- Poner las colas en un array
local queueArr = { q1, q2, q3, q4 }
-- Crea dos punteros que representan los índices de la lectura y añade colas
local readIndex = 1
local addIndex = 1
-- Crea una función local que actualice los índices apropiadamente
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Cree una función local que lea n elementos de la cola
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- bucle a través de cada cola
for i = 1, 4, 1 do
-- determinar si esta cola leerá un objetoadicional
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- lee los elementos de cada cola
-- +1 artículos si coincide con los criterios de lectura 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 función local que elimina n elementos de la cola
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- Crea una función local que añade un elemento a la cola
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- ¡Escribe algo de código!
for i,player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)

Mapas de Hash

Los mapas hash no tienen límites de cantidad de elementos ni de almacenamiento individuales y se fragmentan automáticamente, pero aún así puedes encontrar throttling si los usas mal.

Por ejemplo, considere una experiencia con un mapa hash de datos del juego, almacenado como el valor de una sola clave llamada metadata . Si estos metadatos contienen un objeto anidado con información como el ID del lugar, el número de jugadores y más, cada vez que se necesiten los metadatos, no tendrá más remedio que llamar a GetAsync("metadata") y recuperar el objeto completo. En este caso, todas las solicitudes van a una sola clave y, por lo tanto, a una sola partición.

En lugar de almacenar todos los metadatos como un solo objeto anidado, el mejor enfoque es almacenar cada campo como su propia clave para que el mapa de hash pueda aprovechar la fragmentación automática. Si necesita separar los metadatos del resto del mapa de hash, agregue un prefijo de nombre (por ejemplo, metadata_user_count en lugar de solo user_count ).