メモリストアのベストプラクティス

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

これらのプラクティスを使用して、一時的なデータを整理し、負荷を分散し、メモリストアの問題に対応します。

データ構造のタイプに応じて、MemoryStoreServiceはメモリとデータ構造内のアイテム数に対して制限を強制します。すべてのデータ構造は、グローバルなパーティションごとのリクエスト制限にも制約されています。

キーとデータ構造の設計

データストアのベストプラクティス静的キーのパターンとプレフィックスを使用するを参照してください。これらのパターンをキーとデータ構造の名前の両方に適用し、すべてのサーバーが同じ論理データを同じ場所にルーティングするようにします。

データが有用である期間に一致する有効期限を選択してください。永続的なプレイヤーレコードや有効期限を超えて生存する必要があるデータにはメモリストアを使用しないでください。サービスの選択に関するヘルプについては、データストアとメモリストアの比較を参照してください。

リクエストの失敗を処理する

データストアのベストプラクティス一時的な失敗を再試行するを参照してください。

SetAsyncよりもUpdateAsyncを優先する

データストアのベストプラクティスSetAsyncよりもUpdateAsyncを優先するを参照してください。ハッシュマップとソートマップは、このパターンのためにMemoryStoreHashMap:UpdateAsync()MemoryStoreSortedMap:UpdateAsync()を提供します。

繰り返しリクエストをずらす

データストアのベストプラクティス繰り返しリクエストをずらすを参照してください。

使用状況を監視する

メモリストアの可観測性ダッシュボードを使用して、クォータ使用量、リクエスト量、応答ステータスを監視します。組み込みのメールアラートを確認し、重要なメモリストアメトリクスのためにカスタムアラートを設定し、エラーレポートを使用して失敗を調査します。

容量を増やす前に、リクエスト、アイテムサイズ、有効期限を減らしてください。正当な使用がデフォルトのクォータを超える場合は、拡張サービスを評価してください。

ソートマップとキューの制限を管理する

ソートマップとキューには、アイテムの最大数と最大メモリに制限があります。さらに、これらのデータ構造のアイテムは常に単一のパーティションに存在します。これらのデータ構造へのすべてのリクエストは、同じパーティションへのリクエストです。

ソートマップまたはキューがアイテムまたはメモリの制限に達した場合は、不要なアイテムを手動で削除するか、有効期限ポリシーを追加してください。メモリ制限がスロットリングを引き起こしている場合は、キーと値から不要な情報を削除してアイテムサイズを減らしてください。

すべてのアイテムが必要な場合や、リクエストスループットによるスロットリングが発生している場合、唯一の解決策はシャーディングです。

シャーディングで負荷を分散する

シャーディングは、関連するデータのセットを複数のデータ構造に分散して保存するプロセスです。言い換えれば、既存の高スループットデータ構造を、元のデータセットを含む複数の小さなデータ構造に置き換えることを意味します。

シャーディングの主な課題は、元の機能を維持しながら、データを複数のデータ構造に分散させる方法を見つけることです。

Robloxはすでにハッシュマップをパーティション分けしていますが、リクエストを複数のキーに分散させることでさらにシャーディングできます。

ソートマップのシャーディング

ソートマップでプレイヤーレコードをシャーディングするには、モジュロ算術を使用して各Idを固定数のマップの1つに割り当てます。以下の例では、4つのマップを使用し、同じユーザーを常に同じマップにルーティングします:

ソートマップのシャーディング
-- メモリストアサービスを初期化
local MemoryStoreService = game:GetService("MemoryStoreService")
-- ソートマップバケットを作成
local sm1 = MemoryStoreService:GetSortedMap("sm1")
local sm2 = MemoryStoreService:GetSortedMap("sm2")
local sm3 = MemoryStoreService:GetSortedMap("sm3")
local sm4 = MemoryStoreService:GetSortedMap("sm4")
local sortedMaps = { sm1, sm2, sm3, sm4 }
-- アイテムキーから正しいバケットを取得するヘルパー関数
local function getSortedMapBucket(userId)
local bucketIndex = (userId % #sortedMaps) + 1
return sortedMaps[bucketIndex]
end
-- デフォルト値0でプレイヤーを初期化
for _, player in game:GetService("Players"):GetPlayers() do
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
bucket:SetAsync(tostring(userId), 0, 600)
end
-- プレイヤーの値を取得
local player = game:GetService("Players"):GetPlayers()[1]
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
local playerScore = bucket:GetAsync(tostring(userId))
print(playerScore)

キューのシャーディング

キューのシャーディングは、ソートマップのシャーディングよりも難しいです。リクエストスループットを複数のキューに分散させたい場合でも、追加、読み取り、削除は常にキューの前または後でのみ発生します。

1つの解決策は、回転キューを使用することです。これは、複数のキューを作成し、アイテムを追加または読み取るときにそれらの間で回転することを意味します:

  1. いくつかのキューを作成し、それらを配列に追加します。
  2. 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[i]
queue:RemoveAsync(ids[i])
end
end
-- キューにアイテムを追加するローカル関数を作成
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[addIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- コードを書いてみましょう!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player.User.Id, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)

ハッシュマップ

ハッシュマップには個別のメモリやアイテム数の制限がなく、自動的にシャーディングされますが、使用方法が不適切な場合はスロットリングに遭遇する可能性があります。

たとえば、metadataという名前の単一のキーの値として保存されたデータのハッシュマップを持つゲームを考えてみてください。このメタデータが、プレイスID、プレイヤー数などの情報を含むネストされたオブジェクトを含んでいる場合、メタデータが必要なたびにGetAsync("metadata")を呼び出して全体のオブジェクトを取得する必要があります。この場合、すべてのリクエストが単一のキー、したがって単一のパーティションに送信されます。

すべてのメタデータを単一のネストされたオブジェクトとして保存するのではなく、各独立してアクセスされるフィールドをそれぞれのキーとして保存し、ハッシュマップが自動シャーディングを活用できるようにします。メタデータとハッシュマップの残りの部分の間に分離が必要な場合は、user_countの代わりにmetadata_user_countのような命名プレフィックスを追加します。

1つまたは数個のキーが頻繁にリクエストを受ける場合は、それらの呼び出しを複数のキーにシャーディングします。たとえば、すべてのゲームサーバーが1つのハッシュマップキーから値を取得する場合、リクエストがパーティションスロットリングを引き起こす可能性があります。負荷を減らすために、値を複数のキーにコピーし、各サーバーを安定したシャードにルーティングします。

©2026 Roblox Corporation。Roblox(ロブロックス)、RobloxロゴおよびPowering Imaginationは、米国並びにその他の国における登録商標および非登録商標です。