Dependendo do digitarde estrutura de dados, o MemoryStoreService impõe limites na memória e no número de itens em uma estrutura de dados. Todas as estruturas de dados também são limitadas por um limite global por partição.
Cada experiência do Roblox tem o Painel de Observabilidade do Armazenamento de Memória, que inclui um conjunto de gráficos que você pode usar para monitorar o uso do armazenamento de memória.
Maps e Queues Classificados
Maps e queues classificados ambos têm limites no número máximo de itens e no máximo de memória. Além disso, os itens em uma dessas estruturas de dados sempre são solicitados em uma única partição. Cada solicitação para uma dessas estruturas de dados é uma solicitação para a mesma partição.
Quando um mapa ou fila classificada atinge seu limite de item ou memória, a melhor maneira de agir é remover itens não necessários manualmente ou ao adicionar uma política de expiração para itens. Alternativamente, se apenas o limite de memória estiver causando estratégias de ajuste, você pode tentar reduzir o tamanho dos seus itens ao remover informações não necessárias dos seus chaves e valores.
Se você precisar de todos os seus itens ou estiver experimentando estratégias de otimização de pedido, a única solução é fazer sharding.
Dividindo
Fragmentação é o processo de armazenar um conjunto de dados relacionados em várias estruturas de dados. Em outras palavras, significa usar uma estrutura de dados existente e substituí-la por várias, menores, que juntos contêm o mesmo conjunto de dados que o original.
O desafio chave para sharding é encontrar uma maneira de distribuir os dados em várias estruturas de dados de uma maneira que mantenha a mesma funcionalidade que o original.
Fragmentando um Mapa Classificado
Para cortar um mapa sortido, considere dividir seus dados em subseções alfabéticas com faixas de personagens. Por exemplo, assuma que você só tem chaves com a primeira letra de A-Z e você acredita que quatro subseções sortidas são suficientes para o seu caso de uso atual e futuro:
- O primeiro mapa pode cobrir A-G, o segundo H-N, o terceiro O-T e o quarto U-Z.
- Para inserir ou recuperar um item, use o mapa apropriado com base no personagem de partida do item.
Fragmentando um Mapa Classificado
-- Inicialize o Serviço de Armazenamento de Memória
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crie seus baldes de mapa classificados
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- Função de Helper para recuperar o balde correto da Chave do Item
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
-- Inicialize nomes de jogadores com valor padrão de 0
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Recuperar o valor de um jogador
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)
Dividindo uma Fila
Dividir uma fila é mais complicado do que dividir um mapa ordenado. Embora você queira espalhar o tráfego de pedido em múltiplas filas, adiciona, lê e remove apenas ocorre na frente ou de trás da fila.
Uma solução é usar uma fila giratória, o que significa criar várias filas e girar entre elas quando você adiciona ou lê um item:
Crie várias filas e adicione-as a um matriz / lista.
Crie dois ponteiros locais. Um representa a fila que você deseja ler e remover itens. O outro representa a fila que você deseja adicionar itens:
- Para operações de leitura, calule o número de itens que você precisa de cada fila, bem como onde mover o ponto de leitura.
- Para remover operações, passe os IDs da leitura para cada fila.
- Para adicionar operações, adicione à fila no ponto de adição e aumente o ponto de adição.
Dividindo uma Fila
-- Inicialize o Serviço de Armazenamento de Memória
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Crie suas filas
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- Coloque as filas em um array
local queueArr = { q1, q2, q3, q4 }
-- Crie dois ponteiros representando os índices da leitura e adicionar filas
local readIndex = 1
local addIndex = 1
-- Crie uma função local que atualiza os índices adequadamente
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Crie uma função local que lê n itens da fila
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- iterar através de cada fila
for i = 1, 4, 1 do
-- determinar se essa fila lerá um item extra
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- leia itens de cada fila
-- +1 itens se você atender a critérios de leitura 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
-- Crie uma função local que remove n itens da fila
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- Crie uma função local que adiciona um item à fila
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Escreva algum 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 de Hashes
Mapas de hashes não têm limites de memória ou contagem de itens individuais e são automaticamente sharded, mas você ainda pode encontrar estratégias de otimização se usá-las mal.
Por exemplo, considere uma experiência com um mapa de dados de jogo, armazenado como o valor de uma única chave chamada metadata. Se este metadado contiver um objeto aninhado com informações, como o ID do local, o número de jogadores e muito mais, toda vez que o metadado for necessário, você não terá escolha a não ser chamar GetAsync("metadata") e recuperar todo o Objeto. N
Em vez de armazenar todo o metadado como um único, Objetosubjacente, a melhor abordagem é armazenar cada campo como sua própria chave para que o mapa de hashes possa aproveitar-se do automatismo de corte. Se você precisar de separação entre metadados e o resto do mapa de hashes, adicione um prefixo de nome (por exemplo, metadata_user_count em vez de apenas user_count).