根据数据结构输入,MemoryStoreService 对数据结构中的 限制 在内存和数据结构中的项目数量进行限制。所有数据结构都受到全球每个分区的请求限制。
每个 Roblox 体验都有 Memory Store Observability Dashboard,其包括一组图表,您可以使用它们来监控存储器使用。
排序地图和队列
排序的地图和队列都有限制在最大物品数量和最大总内存上。此外,这些数据结构中的项目总是位于单个 partition上。 对于这些数据结构的每个请求都是对同一個 partition的请求。
排序地图或队列到达其物品或内存上限时,最佳的行动是手动移除不必要的项目或添加过期政策对项目。或者,如果仅是物品上限导致限制,您可以尝试通过删除不必要的项目来减少您的项目的大小。
如果您需要所有您的物品或因请求过度拥挤而导致瓦解,唯一的解决方案是碎片。
碎片
碎片是存储相关数据的一种过程,它的意思是将一组相关数据存储在多个数据结构之间。 在其他 words,它的意思是取一个现有的高通量数据结构,并将其替换为多个小于它们的,这些数据结构中包含相同的数据集。
碎片的关键挑战是找到将数据分布到多个数据结构中的方法,以保持原始数据的功能相同。
碎片排序地图
要将排序的地图分片,请考虑将您的数据拆分为字母范围的字母表。例如,假设您只有从A到Z的字母开始的钥匙,并且您相信四个排序的地图足以为您的当前使用场景和未来的增长提供支持:
- 第一张地图可以覆盖A-G、第二个H-N、第三个O-T和第四个U-Z。
- 要插入或恢复物品,请根据物品的起始角色使用适当的地图。
碎片排序地图
-- 初始化记忆存储服务
local MemoryStoreService = game:GetService("MemoryStoreService")
-- 创建您的排序地图桶
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- 帮助函数从项目钥匙中恢复正确的桶
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
-- 使用默认值 0 初始化玩家名称
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- 检索玩家的值
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)
分割队列
分割队列比分割排序的地图更复杂。虽然您想要将请求流量分布到多个队列上,但添加、读取和移除只在队列的前端或后端发生。
一个解决方案是使用旋转队列,这意味着在添加或读取物品时,创建多个队列并在他们之间旋转:
创建几个队列并将它们添加到数组列。
创建两个本地指针。其中一个表示您想要读取和删除项目的队列。其他一个表示您想要添加项目的队列:
- 对于读取操作,计算您需要从每个队列中的物品数量,以及移动读取指针到哪里。
- 对于移除操作,从读取到每个队列中传递 ID。
- 对于添加操作,在添加指针上添加到队列中,然后将其增加。
分割队列
-- 初始化记忆存储服务
local MemoryStoreService = game:GetService("MemoryStoreService")
-- 创建您的队列
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- 将队列放入阵列
local queueArr = { q1, q2, q3, q4 }
-- 创建两个指针代表读取的索引,并添加队列
local readIndex = 1
local addIndex = 1
-- 创建一个本地函数,更新索引
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- 创建一个本地函数,它可以从队列中读取 n 个项目
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- 循环通过每个队列
for i = 1, 4, 1 do
-- 确定此队列是否读取额外项物品
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- 从每个队列中读取项目
-- 额外满足读取条件的 +1 项
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
-- 创建一个本地函数,从队列中移除 n 个项目
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- 创建一个本地函数,将一个物品添加到队列
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- 写一些代验证码!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)
哈希地图
哈希地图不具有单个内存或物品数量限制,并且自动分裂,但您仍然可以因使用它们而遇到限速,如果您滥用它们。
例如,考虑一个拥有游戏数据的哈希地图的体验,存储为单个名为 metadata 的键值。 如果此元数据包含类似于位置 ID、玩家数量等信息,每次需要元数据时,您都无法有选择,只能调用 GetAsync("metadata") 并恢复整个对象。 在这种情况下,所有请求都会发生到一个键值
而不是将所有数据存储为一个单一的、递归的对象,更好的方法是将每个字段作为自己的钥匙存储,以便可以利用自动碎片的优势。如果您需要将数据和其他哈希地图区域之间进行分离,请添加命名前缀(例如 metadata_user_count )。