記憶體儲存

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

MemoryStoreService 是一個高吞吐量和低延遲的數據服務,提供快速的內存數據存儲,所有伺服器在實時會話中均可訪問。記憶體儲存 適合於頻繁且短暫的數據,這些數據變化迅速且不需要持久性,因為它們的訪問速度更快,並在達到最大壽命時消失。對於需要在會話之間持久化的數據,請使用 數據儲存。

數據結構

記憶體儲存有三種原始數據結構,這些結構在伺服器之間共享以便快速處理,而不是直接訪問原始數據:排序映射、隊列 和 哈希映射。每種數據結構都適合某些用例:

  • 基於技能的配對 - 在伺服器之間的共享 隊列 中保存用戶信息,例如技能水平,並使用大廳伺服器定期進行配對。
  • 跨伺服器交易和拍賣 - 允許不同伺服器之間的通用交易,用戶可以對實時變化的價格進行出價,使用 排序映射 的鍵值對。
  • 全球排行榜 - 在共享的 排序映射 中存儲和更新用戶排名。
  • 共享庫存 - 在共享的 哈希映射 中保存庫存物品和統計數據,用戶可以同時使用庫存物品。
  • 持久數據的緩存 - 將您的持久數據同步並複製到可以作為緩存的記憶體儲存 哈希映射 中,以提高遊戲性能。

一般來說,如果您需要根據特定鍵訪問數據,請使用哈希映射。如果您需要數據有序,請使用排序映射。如果您需要以特定順序處理數據,請使用隊列。

限制和配額

為了維持可擴展性和系統性能,記憶體儲存對內存大小、API 請求和數據結構大小有數據使用配額。

記憶體儲存有一個基於過期時間的驅逐策略,也稱為生存時間(TTL)。項目在過期後被驅逐,並釋放內存配額以供新條目使用。當您達到內存限制時,增加內存使用的請求將失敗,直到您釋放內存或增加配額。請參見 從內存配額耗盡中恢復 以獲取選項。

內存大小配額

內存配額限制遊戲可以消耗的總內存量。這不是固定值;而是根據遊戲中用戶的數量隨時間變化,根據公式 64 KB + 1.2 KB * [用戶數量]。該配額適用於遊戲級別,而不是伺服器級別。

當用戶加入遊戲時,額外的內存配額會立即可用。當用戶離開遊戲時,配額不會立即減少。配額在八天的回溯期內重新評估為較低的值。

當您的遊戲達到內存大小配額時,任何增加內存大小的 API 請求都會失敗。減少或不改變內存大小的請求仍然成功。

使用 可觀察性 儀表板,您可以實時查看遊戲的內存大小配額,使用 內存使用情況 圖表。

API 請求限制

請求單位 配額適用於所有 MemoryStoreService API 調用。該配額為 1000 + 120 * [同時用戶數量] 每分鐘請求單位。

大多數 API 調用僅消耗一個請求單位,少數例外:

請求配額也適用於遊戲級別,而不是伺服器級別。這提供了靈活性,可以在伺服器之間分配請求,只要總請求速率不超過配額。如果您超過配額,當服務限制您的請求時,您將收到錯誤響應。

使用 可觀察性 功能,您可以實時查看遊戲的請求單位配額。

數據結構大小限制

對於單個排序映射或隊列,適用以下大小和項目數量限制:

  • 最大項目數:1,000,000
  • 最大總大小(包括排序映射的鍵):100 MB

每分區限制

除了遊戲級別的 請求單位配額 外,記憶體儲存對每個分區應用請求限制,以保護所有遊戲的服務穩定性。這些限制不是分配給您的遊戲的配額,也不是您遊戲可以實現的吞吐量的硬上限。以下值是估算值。它們在 Roblox 後端配置,可能會變更,因此不要將其設計為固定數字。

每個請求都計入持有項目的 分區 的限制,無論數據結構如何。您目前可以預期在單個分區的請求速率約為 30,000 請求單位每分鐘 開始進行限制,因此請努力保持在該速率之下。數據結構消耗該限制的多少取決於其項目的分佈:

  • 排序映射和隊列 各自位於單個分區,因此對這些數據結構的每個請求都計入相同的分區限制。
  • 哈希映射 將其項目分散到多個分區,因此跨多個項目鍵的流量很少接近任何一個分區的限制。

哈希映射對每個項目鍵有額外的限制,大約為 5,000 寫請求單位每分鐘 和 15,000 讀請求單位每分鐘。這個每鍵限制適用於每個分區限制之上,因此經常訪問的鍵可能會觸及其中一個。每鍵限制僅適用於單鍵操作:讀取限制適用於 MemoryStoreHashMap:GetAsync(),寫入限制適用於 MemoryStoreHashMap:SetAsync() 和 MemoryStoreHashMap:RemoveAsync(),而 MemoryStoreHashMap:UpdateAsync() 計入讀取和寫入限制。MemoryStoreHashMap:ListItemsAsync() 掃描分區而不是單個鍵,因此僅適用於每分區限制。

如果您不需要排序或先進先出功能,哈希映射通常是最佳選擇,因為它可以將負載分散到分區之間。

當請求超過分區或每鍵限制時,服務會限制它們並返回 PartitionRequestsOverLimit 狀態碼,您可以使用 可觀察性 儀表板進行監控。

如果您預期某個數據結構或項目鍵會接收高且持續的請求速率,請 分片 以便負載分散到更多的分區或鍵。均勻地在多個項目鍵之間分配讀取和寫入,以保持在每鍵限制之內。您還可以通過在伺服器上緩存值並在一段時間後重新檢查它們、在可能的情況下批量請求以及在收到限制響應時應用 指數退避 來減少請求速率。

從內存配額耗盡中恢復

如果您的遊戲收到 TotalMemoryOverLimit 錯誤,請檢查 可觀察性儀表板 中的 內存使用情況 圖表。要恢復增加內存使用的寫入,請刪除不需要的項目,等待項目過期,或使用以下選項之一。

使用開放雲刷新記憶體儲存

如果您不再需要存儲的數據,請使用開放雲 刷新記憶體儲存 端點異步清除所選範圍內的所有記憶體儲存數據結構。

要刷新記憶體儲存:

  1. 創建 API 密鑰,以獲得刷新記憶體儲存和獲取目標宇宙的刷新操作狀態的權限。
  2. 向 https://apis.roblox.com/cloud/v2/universes/{universe_id}/memory-store:flush 發送 POST 請求,並在 x-api-key 標頭中包含 API 密鑰。將 {universe_id} 替換為您的遊戲宇宙 ID。將 scope 查詢參數設置為 LIVE 以獲取生產數據,或設置為 TEST 以獲取 Studio 數據。如果省略 scope,則端點使用 LIVE。
  3. 使用 獲取記憶體儲存刷新操作 輪詢刷新請求返回的操作,直到 done 為 true,並檢查是否有 error。輪詢時使用相同的範圍。返回的 path 已經包含 cloud/v2,因此在 https://apis.roblox.com/{path} 輪詢時無需添加其他前綴。

Studio 和生產記憶體儲存是 隔離的。刷新一個範圍不會清除另一個範圍。在清除不需要的數據後,遵循 最佳實踐,例如縮短過期時間和明確刪除,以防止內存使用再次達到配額。

使用擴展服務增加容量

如果您的遊戲需要保留數據並使用超過默認配額的更多存儲,請為記憶體儲存啟用 擴展服務。擴展服務允許您為超過默認配額的記憶體儲存和請求付費。

要開始,滿足 資格要求,添加帳戶詳細信息和付款方式,然後在 創作者儀表板 中選擇您的遊戲。在 擴展服務 下,啟用記憶體儲存並設置所需資源的每月預算。請參見 啟用服務 和 服務定價 以獲取詳細信息。

如果使用超過您的預算,擴展服務會限制該服務。監控使用情況和計費,並繼續 刪除不需要的數據。

最佳實踐

為了保持您的內存使用模式最佳,並避免達到 限制,請遵循以下最佳實踐:

  • 刪除已處理的項目。 使用 MemoryStoreQueue:RemoveAsync() 方法定期清理已讀取的項目,對於隊列和 MemoryStoreSortedMap:RemoveAsync() 對於排序映射,可以釋放內存並保持數據結構的最新狀態。

  • 在添加數據時將過期時間設置為最小的時間範圍。 雖然 MemoryStoreQueue:AddAsync() 和 MemoryStoreSortedMap:SetAsync() 的默認過期時間為 45 天,但設置最短的時間可以自動清理舊數據,以防止它們填滿您的內存使用配額。

    • 不要存儲大量數據並設置長過期時間,因為這會冒著超過內存配額的風險,並可能導致問題,甚至破壞整個遊戲。
    • 始終明確刪除不需要的項目或設置短項目過期時間。
    • 通常,您應該使用明確刪除來釋放內存,並將項目過期作為安全機制,以防止未使用的項目長時間佔用內存。
  • 只在內存中保留必要的值。

    例如,對於拍賣行遊戲,您只需要維護最高出價。您可以在一個鍵上使用 MemoryStoreSortedMap:UpdateAsync() 來保持最高出價,而不是在數據結構中保留所有出價。

  • 使用 指數退避 來幫助保持在 API 請求限制之下。

    例如,如果您收到 DataUpdateConflict,您可以在兩秒後重試,然後是四秒、八秒等,而不是不斷向 MemoryStoreService 發送請求以獲取正確的響應。

  • 通過 分片 將大型數據結構拆分為多個較小的數據結構。

    通常在較小的結構中管理數據比將所有內容存儲在一個大型數據結構中更容易。這種方法還可以幫助避免使用和速率限制。例如,如果您有一個使用前綴的排序映射,考慮將每個前綴分開到自己的排序映射中。對於特別受歡迎的遊戲,您甚至可以根據用戶 ID 的最後幾位數字將用戶分開到多個映射中。

  • 在哈希映射中使用多個鍵的副本來 分片 經常訪問的鍵,以分散負載。

  • 壓縮存儲的值。

    例如,考慮使用 LZW 算法來減少存儲值的大小。

  • 註冊擴展服務。

    對於超過默認配額的存儲或請求使用,請 啟用記憶體儲存的擴展服務。

可觀察性

可觀察性儀表板 提供有關監控和故障排除您的記憶體儲存使用情況的見解和分析。通過實時更新的圖表,您可以跟踪遊戲的內存使用模式,查看當前分配的配額,監控 API 狀態,並識別潛在的性能優化問題。

以下表格列出了可觀察性儀表板的 按狀態請求計數 和 按 API x 狀態請求 圖表中所有 API 響應的狀態碼及其描述。要獲取有關如何解決這些錯誤的更多信息,請參見 故障排除。有關錯誤相關的具體配額或限制,請參見 限制和配額。

狀態碼描述
成功成功。
DataStructureMemoryOverLimit超過數據結構級別內存大小限制(100 MB)。
DataUpdateConflict由於並發更新而發生衝突。
AccessDenied未經授權訪問遊戲數據。此請求不消耗請求單位或使用配額。
InternalError內部錯誤。
InvalidRequest請求缺少所需信息或信息格式錯誤。
DataStructureItemsOverLimit超過數據結構級別項目計數限制(1M)。
NoItemFound在 MemoryStoreQueue:ReadAsync() 或 MemoryStoreSortedMap:UpdateAsync() 中未找到項目。ReadAsync() 每 2 秒輪詢一次,並在找到隊列中的項目之前返回此狀態碼。
DataStructureRequestsOverLimit超過數據結構級別請求單位限制(每分鐘 100,000 請求單位)。
PartitionRequestsOverLimit超過 每分區或每鍵請求單位限制。
TotalRequestsOverLimit超過宇宙級別請求單位限制。
TotalMemoryOverLimit超過宇宙級別內存配額。
ItemValueSizeTooLarge值大小超過限制(32 KB)。

以下表格列出了客戶端的狀態碼,這些狀態碼目前在可觀察性儀表板上不可用。

狀態碼描述
InternalError內部錯誤。
UnpublishedPlace您必須發布此地點才能使用 MemoryStoreService。
InvalidClientAccessMemoryStoreService 必須從伺服器調用。
InvalidExpirationTime字段 'expiration' 時間必須在 0 和 3,888,000 之間。
InvalidRequest無法將值轉換為 json。
InvalidRequest無法將 sortKey 轉換為有效的數字或字符串。
TransformCallbackFailed無法調用轉換回調函數。
RequestThrottled最近的 MemoryStores 請求達到一個或多個限制。
UpdateConflict超過最大重試次數。

故障排除

以下表格列出了每個響應狀態碼的建議解決方案:

錯誤故障排除選項
DataStructureRequestsOverLimit / PartitionRequestsOverLimit
  • 通過將信息保存到另一個變量並在一定時間間隔(例如 30 秒)後重新檢查來添加本地緩存。
  • 使用 按狀態請求計數 圖表來驗證您收到的 成功 響應是否多於 未找到項目。限制您對 MemoryStoreService 的失敗請求的次數。
  • 在請求之間實施短暫延遲。
  • 遵循 最佳實踐,包括:
    • 如果您收到大量 DataStructureRequestsOverLimit/PartitionRequestsOverLimit 響應,請對數據結構進行分片。
    • 如果您在哈希映射調用中收到大量 PartitionRequestsOverLimit 響應,請對哈希映射鍵進行分片。
    • 如果您看到 PartitionRequestsOverLimit 響應,請減少或批量調用特定數據結構或哈希映射鍵。
    • 實施指數退避以找到合理的請求速率。
TotalRequestsOverLimit
DataStructureItemsOverLimit
DataStructureMemoryOverLimit
TotalMemoryOverLimit
DataUpdateConflict
  • 在請求之間實施短暫延遲,以避免多個請求同時更新相同的鍵。
  • 對於排序映射,使用 MemoryStoreSortedMap:UpdateAsync() 方法上的回調函數,在一定次數的嘗試後中止請求,如以下代碼示例所示:
  • 中止請求的示例
    local MemoryStoreService = game:GetService("MemoryStoreService")
    local map = MemoryStoreService:GetSortedMap("AuctionItems")
    function placeBid(itemKey, bidAmount)
    map:UpdateAsync(itemKey, function(item)
    item = item or { highestBid = 0 }
    if item.highestBid < bidAmount then
    item.highestBid = bidAmount
    return item
    end
    print("item is "..item.highestBid)
    return nil
    end, 1000)
    end
    placeBid("MyItem", 50)
    placeBid("MyItem", 40)
    print("done")
  • 調查您是否有效地調用 MemoryStoreService 以避免衝突。理想情況下,您不應該過度發送請求。
  • 在使用 MemoryStoreQueue:RemoveAsync() 方法對隊列進行讀取後,定期刪除項目,並使用 MemoryStoreSortedMap:RemoveAsync() 對排序映射進行刪除。
內部錯誤
無效請求
  • 確保在請求中包含正確和有效的參數。無效參數的示例包括:
    • 空字符串
    • 超過長度限制的字符串
ItemValueSizeTooLarge
  • 將項目值分片或拆分為多個鍵。
    • 要組織分組鍵,按字母順序對它們進行排序,通過在鍵中添加 prefix。
  • 編碼或壓縮存儲的值。

在 Studio 中測試和調試

MemoryStoreService 中的數據在 Studio 和生產之間是隔離的,因此在 Studio 中更改數據不會影響生產行為。這意味著您從 Studio 的 API 調用不會訪問生產數據,允許您在進入生產之前安全地測試記憶體儲存和新功能。

Studio 測試具有與生產相同的 限制和配額。對於基於用戶數量計算的配額,結果配額可能非常小,因為您是 Studio 測試的唯一用戶。在 Studio 測試時,您可能還會注意到與生產使用相比,延遲略高且錯誤率上升,這是由於進行了一些額外檢查以驗證訪問和權限。

有關如何在實時遊戲或在 Studio 測試中調試記憶體儲存的信息,請使用 開發者控制台。

©2026 Roblox Corporation、Roblox、Roblox 標誌及 Powering Imagination 是我們在美國及其他國家地區的部分註冊與未註冊商標。