設計記憶體資料結構時的最佳實踐

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

依據資料結構輸入,MemoryStoreService 會在記憶體和數據結構中強制執行 限制 。所有資料結構都受到全球 per-Partition 請求限制。

每個 Roblox 體驗都有 記憶體儲存監視儀梯盤,這包括一套圖形,可以用於監視記憶體儲存使用。

排序地圖和佇列

排序的地圖和排隊都有限制在最大物品數量和最大總記憶體上。此外,這些資料結構中的項目總是位於單一的 partition上。 對於這些資料結構中的每個要求都是對同一個 partition 的請求。

排序樹或佇列達到其項目或內存限制時,最佳的行動是手動移除不必要的項目或添加過期政策對項目。 如果僅是內存限制造成減速,您可以嘗試將項目的大小縮小,例如從鑰匙和值中移除不必要的信息。

如果您需要所有物品或正在因為請求輸出而感到瓦解,唯一的解決方案是碎片。

碎片

碎片是存儲一組相關數據的集合在多個數據結構之間的過程。它的意思是將原來的高通量數據結構擷取並將其重新擷取為多個更小的集合,這些集合中包含相同的數據集。

碎片的關鍵挑戰是尋找一種方法來在多個資料結構中將資料分布到一個方式,以保持原來的功能。

分裂排序的地圖

要從排序的地圖中分裂,請考慮將您的資料分為 alphabetic 子區,並且設定角色範圍。例如,假設您只有 A 到 Z 的字母,您將擁有四個排序的地圖即可滿足您目前的使用情況和未來的成長:

  • 第一張地圖可以覆蓋 A 、G 、N 和 U ,第二張地圖可以覆蓋 H 和 O ,第三張地圖可以覆蓋 T 和 U 。
  • 要插入或取回物道具,請使用基於物道具起始角色的相應地圖。
分裂排序的地圖

-- 初始化記憶體服務
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)

擷取佇列中的項目

分裂排序圖是複排排序圖的一個更複雜的情況。 雖然您想要將要求輸出通過多個排序圖分配,但添加、閱取和移除只會在排序圖的前端或後端發生。

一個解決方案是使用旋轉排隊列,這意味著在添加或檢查項道具時,可以在多個排隊中旋轉:

  1. 建立多個隊列,並將它們添加到陣列。

  2. 創建兩個本地指针。其中一個代表您想要閱取並從中移除項目的佇列。其他一個代表您想要添加項目的佇列:

    • 對於閱取操作,請計算每個佇列中你需要的物品數量,並且將閱取指向器移動到哪裡。
    • 對於移除操作,將 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)

哈希地圖

Hash 地圖沒有個別的記憶體或項目數量限制,並且自動分裂,但如果您不小心使用它們,您還是可以遇到瓶頸。

舉例來說,考慮使用一個存儲為 metadata 值的遊戲資料的哈希地圖。如果此元件包含帶有位置 ID、玩家數量等資訊的樹狀結構,每次需要元件時,您就沒有選擇,只能召用 GetAsync("metadata") 並取回整個元物件。在這

而不是儲存所有元料為單一、雙重疊的對物件,更好的方法是將每個字段作為自己的鑰匙來儲存,以便斷片圖可以利用自動斷片。 如果您需要在元料和剩餘斷片圖之間進行分離,請添加命名預碼 (例如 metadata_user_count 而不是只有 user_count