根据数据结构类输入,MemoryStoreService 对数据结构中的内存和物品数量限制 限制。所有数据结构也受到全球每个分区请求限制的约束。
每个 Roblox 体验都有 内存存储观察仪仪表板 ,包括一组你可以使用来监控内存存储使用的图表。
排序地图和队列
排序的地图和队列都有最大项目数和最大总内存的限制。此外,这些数据结构中的物品总是居住在单个分区上。向这些数据结构中的任何一个请求都是向同一分区的请求。
当排序的地图或队列达到其物品或内存限制时,最好的行动是手动删除不需要的物品或通过添加到期政策来延长物品的有效期。或者,如果仅限于内存限制导致瓶颈,您可以尝试通过从钥匙和值中删除不需要的信息来减少项目的大小。
如果您需要所有物品或由于请求带宽限制而遇到瓶颈,唯一的解决方案是碎片化。
碎片化
碎片化是将一组相关数据存储在多个数据结构中的过程。换言之,这意味着采用现有、高吞吐量数据结构,并将其替换为包含相同数据集的多个、更小的结构,这些结构共同构成原始数据集。
碎片化的关键挑战是找到一种方法来将数据分布到多个数据结构上,使其保持与原始相同的功能。
分割排序的地图
要碎片排序的地图,考虑将数据分割为具有字符范围的字母分段。例如,假设您只有 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而不是只有user_count).