設計記憶儲存資料結構時的最佳做法

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

根據數據結構輸入,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)

分割隊列

分割隊列比分割排序地圖更複雜。雖然您想將請求處理量分散到多個隊列上,添加、讀取和移除只會發生在隊列的前部或後部。

一個解決方案是使用旋轉隊列,這意味著創建多個隊列並在添加或閱讀項道具時之間旋轉:

  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)

哈希地圖

哈希地圖沒有個別記憶或項目數限制,並且會自動碎片化,但如果您使用它們不當,仍然可能遇到限制。

例如,考慮使用一個含有遊戲數據的哈希地圖的體驗,存儲為單個名為 metadata 的關鍵值。如果此元數據包含包含地點ID、玩家數量等信息的嵌套對象,每次需要元數據時,您都無法選擇調用GetAsync("metadata")並領取整個對物件。在這種情況下,所有請求都會前往單一鑰匙,因此單一分區。

而不是將所有元數據存儲為單一、嵌套對物件,更好的方法是將每個字段存儲為自己的鑰匙,以便哈希地圖可以利用自動碎片化。如果您需要將元數據和其他哈希地圖分開,請添加命名前缀(例如)而不是只有》)。