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 restritas por um limite global por partição.
Cada experiência do Roblox tem o Painel de Observabilidade da Loja de Memória, que inclui um conjunto de gráficos que você pode usar para monitorar o uso da loja de memória.
Mapas e filas ordenados
Mapas e filas ordenados ambos têm limites no número máximo de itens e memória total máxima.Além disso, os itens em uma dessas estruturas de dados sempre residem em uma única partição.Cada solicitação para uma dessas estruturas de dados é um pedido para a mesma partição.
Quando um mapa ou filas classificadas atingem seu limite de itens ou memória, a melhor ação a ser tomada é remover itens desnecessários manualmente ou adicionando uma política de expiração para itens.Alternativamente, se apenas o limite de memória estiver causando engarrafamento, você pode tentar reduzir o tamanho de seus itens ao retirar informações desnecessárias de suas chaves e valores.
Se você precisar de todos os seus itens ou estiver experimentando um atraso devido ao tráfego de solicitações, a única solução é a fragmentação.
Fragmentação
Sharding é o processo de armazenar um conjunto de dados relacionados em várias estruturas de dados.Em outras palavras, significa tomar uma estrutura de dados de alto volume existente e substituí-la por várias, menores, que, juntas, contêm o mesmo conjunto de dados do original.
O desafio principal ao sharding é encontrar uma maneira de distribuir os dados em várias estruturas de dados de maneira que mantenha a mesma funcionalidade do original.
Dividindo um mapa ordenado
Para fragmentar um mapa ordenado, considere dividir seus dados em subseções alfabéticas com alcances de caracteres.Por exemplo, assuma que você só tem chaves com a primeira letra de A-Z e acredita que quatro mapas classificados são suficientes para o seu caso de uso atual e crescimento 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 inicial do item.
Dividindo um Mapa Ordenado
-- Inicializar 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 auxílio para recuperar o balde correto a partir da Chave de 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
-- Inicializar 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)
Fragmentar uma fila
Fragmentar uma fila é mais complicado do que fragmentar um mapa ordenado.Embora você queira distribuir a largura de solicitações através de várias filas, adições, leituras e remoções ocorrem apenas na frente ou na parte de trás da fila.
Uma solução é usar uma fila rotativa, o que significa criar várias filas e alternar entre elas quando você adicionar ou ler um item:
Crie várias filas e adicione-as a um matriz / lista.
Crie dois pontos locais. Um representa a fila que você deseja ler e remover itens de. O outro representa a fila para a qual você deseja adicionar itens:
- Para operações de leitura, calcule o número de itens que você precisa de cada fila, bem como onde mover o ponteiro 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 ponteiro.
Fragmentar uma Fila
-- Inicializar 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 pontos que representam os índices da leitura e adição de filas
local readIndex = 1
local addIndex = 1
-- Crie uma função local que atualize os índices adequadamente
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Crie uma função local que leia n itens da fila
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- 循环 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]
-- ler itens de cada fila
-- +1 itens se corresponder a critérios de leitura extras
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 hash não têm limites de memória ou contagem de itens individuais e são automaticamente fragmentados, mas você ainda pode encontrar engarrafamentos se usá-los mal.
Por exemplo, considere uma experiência com um mapa de hashes de dados de jogo, armazenado como o valor de uma única chave chamada metadata .Se esse metadado contiver um objeto aninhado com informações, como ID de lugar, contagem de jogadores e muito mais, toda vez que o metadado for necessário, você não tem escolha a não ser chamar GetAsync("metadata") e recuperar todo o Objeto.Neste caso, todas as solicitações vão para uma única chave e, portanto, para uma única partição.
Em vez de armazenar todo o metadado como um único Objetoaninhado, a abordagem mais adequada é armazenar cada campo como sua própria chave para que o mapa de hash possa aproveitar o sharding automático.Se você precisar de separação entre metadados e o resto do mapa de hash, adicione um prefixo de nome (por exemplometadata_user_count em vez de apenas user_count).