データ構造タイプに応じて、MemoryStoreService は 制限 をメモリとデータ構造のアイテム数に課します。すべてのデータ構造も、グローバルなパーティションごとのリクエスト制限によって制限されます。
各 Roblox 経験には、メモリストア観測ダッシュボード があり、メモリストア使用をモニタリングするために使用できるチャートのセットが含まれています。
ソートされたマップとキュー
ソートされたマップとキューの両方には、最大アイテム数と最大総メモリの制限があります。さらに、これらのデータ構造の 1つのアイテムは、常に単一のパーティションに存在します。それらのデータ構造のいずれかに対するリクエストは、同じパーティションに対するリクエストです。
ソートされたマップまたはキューがアイテムまたはメモリ制限に達したとき、不必要なアイテムを手動で削除するか、アイテムに期限ポリシーを追加することが最善の行動です。代わりに、メモリ制限だけがスロットリングを引き起こしている場合は、キーと値から不要な情報を削除して、アイテムのサイズを減らすことができます。
すべてのアイテムが必要な場合やリクエストスループットによる制限が発生している場合、唯一の解決策はシャーディングです。
シャード
シャーディングは、複数のデータ構造にわたって関連するデータのセットを保存するプロセスです。言い換えれば、既存の高速データ構造を取り、同じデータセットを原文と同じく含む複数の小さな構造に置き換えることを意味します。
シャーディングの主な課題は、オリジナルと同じ機能を維持しながら、複数のデータ構造にデータを分散する方法を見つけることです。
ソートされたマップを分割する
ソートされたマップを破片化するには、文字範囲のあるアルファベット順のサブセクションにデータを分割することを検討してください。たとえば、A-Z の最初の文字のみのキーしか持っていないと仮定し、4つのソートされたマップが現在の使用ケースと将来の成長に十分であると信じてください:
- 最初のマップは 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つのローカルポイントを作成します。1つは、アイテムを読み込み、削除したいクエイを表します。もう1つは、アイテムを追加したいクエイを表します:
- 読み込み操作の場合、各キューから必要なアイテム数と、読み込みポインタを移動する場所を計算します。
- 操作を削除するには、読み取りから各キューに 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 }
-- 読み取りと追加のキューのインデックスを表す 2つのポインタを作成
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 ではない).