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 tipo de estructura de datos, MemoryStoreService impone límites sobre la memoria 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 juego de Roblox cuenta con el Tablero de Observabilidad de Memory Store, que incluye un conjunto de gráficos que puedes utilizar para monitorear el uso del almacén de memoria.

Mapas ordenados y colas

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

Cuando un mapa ordenado o una cola alcanza su límite de elementos o de memoria, la mejor acción es eliminar manualmente los elementos innecesarios o agregar una política de expiración para los elementos. Alternativamente, si solo el límite de memoria está causando limitaciones, puedes intentar reducir el tamaño de tus elementos eliminando información innecesaria de tus claves y valores.

Si necesitas todos tus elementos o estás experimentando limitaciones debido al rendimiento de solicitudes, la única solución es el sharding.

Sharding

El sharding 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 con alto rendimiento y reemplazarla por varias más pequeñas que juntas contengan el mismo conjunto de datos que la original.

El desafío clave del sharding es encontrar una manera de distribuir los datos entre múltiples estructuras de datos de manera que mantenga la misma funcionalidad que la original.

Para los mapas hash, aunque la estructura de datos ya está particionada, el sharding se realiza distribuyendo las solicitudes entre varias claves.

Sharding de un mapa ordenado

Para shardear un mapa ordenado, considera dividir tus datos en subsecciones alfabéticas con rangos de caracteres. Por ejemplo, supón que solo tienes claves con la primera letra de la A a la Z, y crees que cuatro mapas ordenados son suficientes para tu caso de uso actual y el crecimiento futuro:

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

-- Inicializa el Servicio de MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crea tus cubos de Mapa Ordenado
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 auxiliar para recuperar el cubo correcto de la Clave del 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
-- Inicializa los nombres de los jugadores con valor predeterminado de 0
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Recupera el valor de un jugador
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)

Sharding de una cola

El sharding de una cola es más complicado que el sharding de un mapa ordenado. Aunque deseas distribuir el rendimiento de solicitudes entre múltiples colas, las adiciones, lecturas y eliminaciones solo ocurren al frente o al final de la cola.

Una solución es usar una cola rotativa, lo que significa crear múltiples colas y rotar entre ellas cuando agregas o lees un elemento:

  1. Crea varias colas y agrégalas a un arreglo.

  2. Crea dos punteros locales. Uno representa la cola desde la que deseas leer y eliminar elementos. El otro representa la cola a la que deseas agregar elementos:

    • Para operaciones de lectura, calcula la cantidad de elementos que necesitas de cada cola, así como dónde mover el puntero de lectura.
    • Para operaciones de eliminación, pasa los IDs desde la lectura a cada cola.
    • Para operaciones de adición, agrega a la cola en el puntero de adición y aumenta el puntero.
Sharding de una Cola

-- Inicializa el Servicio de MemoryStore
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")
-- Pon las Colas en un Arreglo
local queueArr = { q1, q2, q3, q4 }
-- Crea dos punteros representando los índices de las colas de lectura y adición
local readIndex = 1
local addIndex = 1
-- Crea una función local que actualiza los índices apropiadamente
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Crea una función local que lee 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
-- determina si esta cola leerá un elemento extra
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- leer elementos de cada cola
-- +1 elementos 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 agrega 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 _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)

Mapas hash

Los mapas hash no tienen límites individuales de memoria o de recuento de elementos y están automáticamente shardados, pero aún puedes encontrar limitaciones si los usas de manera inadecuada.

Por ejemplo, considera un juego con un mapa hash de datos, almacenados como el valor de una única clave llamada metadata. Si este metadato contiene un objeto anidado con información como ID de lugar, recuento de jugadores y más, cada vez que se necesita el metadato, no tienes otra opción que llamar a GetAsync("metadata") y recuperar todo el objeto. 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 hash pueda aprovechar el sharding automático. Si necesitas separación entre los metadatos y el resto del mapa hash, agrega un prefijo de nomenclatura (por ejemplo, metadata_user_count en lugar de solo user_count).

Además, si una o pocas claves se están accediendo con frecuencia, es importante shardear estas llamadas entre muchas claves. Por ejemplo, si todos los servidores del juego deben recuperar un valor de una clave de mapa hash, puedes encontrarte con limitaciones de partición. Para evitar esto, puedes distribuir esas llamadas entre varias claves con el mismo valor hasta que las limitaciones de partición ya no ocurran.

©2026 Roblox Corporation. Roblox, el logotipo de Roblox y "Powering Imagination" son algunas de nuestras marcas registradas y no registradas en los Estados Unidos y otros países.