MemoryStoreService は、高スループットで低遅延のデータサービスで、ライブセッション中のすべてのサーバーからアクセス可能な迅速なメモリ内データストレージを提供します。 メモリストア は、頻繁に変化し、耐久性を必要としない一時的なデータに適しており、アクセスが速く、最大寿命に達すると消失します。セッションをまたいで持続する必要があるデータには、データストアを使用してください。
データ構造
生データに直接アクセスする代わりに、メモリストアにはサーバー間で迅速に処理できる3つの基本データ構造があります: ソートマップ、キュー、および ハッシュマップ。各データ構造は特定のユースケースに適しています:
- スキルベースのマッチメイキング - サーバー間で共有されるキューにユーザー情報(スキルレベルなど)を保存し、ロビーサーバーを使用して定期的にマッチメイキングを実行します。
- クロスサーバー取引とオークション - ユーザーがリアルタイムで変化する価格でアイテムに入札できるように、異なるサーバー間でのユニバーサルトレーディングを可能にし、キーと値のペアのソートマップを使用します。
- グローバルリーダーボード - 共有リーダーボード内でユーザーのランキングを保存および更新します。
- 共有インベントリ - ユーザーが互いにインベントリアイテムを同時に利用できるように、共有ハッシュマップにインベントリアイテムと統計を保存します。
- 永続データのキャッシュ - データストアの永続データをメモリストアのハッシュマップに同期およびコピーし、キャッシュとして機能させてゲームのパフォーマンスを向上させます。
一般的に、特定のキーに基づいてデータにアクセスする必要がある場合は、ハッシュマップを使用します。データを順序付ける必要がある場合は、ソートマップを使用します。特定の順序でデータを処理する必要がある場合は、キューを使用します。
制限とクォータ
スケーラビリティとシステムパフォーマンスを維持するために、メモリストアにはメモリサイズ、APIリクエスト、およびデータ構造サイズに関するデータ使用クォータがあります。
メモリストアには、TTL(生存時間)とも呼ばれる有効期限に基づく排出ポリシーがあります。アイテムは期限が切れた後に排出され、新しいエントリのためにメモリクォータが解放されます。メモリ制限に達すると、メモリ使用量を増加させるリクエストは、メモリを解放するかクォータを増加させるまで失敗します。オプションについては、メモリクォータの枯渇からの回復を参照してください。
メモリサイズクォータ
メモリクォータは、ゲームが消費できるメモリの総量を制限します。これは固定値ではなく、ゲーム内のユーザー数に応じて時間とともに変化します。計算式は 64 KB + 1.2 KB * [ユーザー数] です。クォータはサーバーレベルではなく、ゲームレベルに適用されます。
ユーザーがゲームに参加すると、追加のメモリクォータがすぐに利用可能になります。ユーザーがゲームを離れると、クォータはすぐには減少しません。クォータが低い値に再評価されるまで、8日間のトレースバック期間があります。
ゲームがメモリサイズクォータに達すると、メモリサイズを増加させるAPIリクエストは常に失敗します。メモリサイズを減少させるか変更しないリクエストは成功します。
可観測性ダッシュボードを使用すると、メモリ使用量チャートを使用して、ゲームのメモリサイズクォータをリアルタイムで確認できます。
APIリクエスト制限
リクエストユニットクォータは、すべての MemoryStoreService API呼び出しに適用されます。このクォータは、1000 + 120 * [同時ユーザー数] リクエストユニット/分です。
ほとんどのAPI呼び出しは1リクエストユニットしか消費しませんが、いくつかの例外があります:
MemoryStoreSortedMap:GetRangeAsync()
戻り値のアイテム数に基づいてユニットを消費します。たとえば、このメソッドが10アイテムを返す場合、呼び出しは10リクエストユニットとしてカウントされます。空の応答を返す場合は、1リクエストユニットとしてカウントされます。
戻り値のアイテム数に基づいてユニットを消費します。MemoryStoreSortedMap:GetRangeAsync()と同様ですが、読み取り中に2秒ごとに追加のユニットを消費します。最大読み取り時間は waitTimeout パラメータで指定します。
MemoryStoreHashMap:UpdateAsync()
最低2ユニットを消費します。
MemoryStoreHashMap:ListItemsAsync()
[スキャンされたパーティション数] + [返されたアイテム] ユニットを消費します。
リクエストクォータもサーバーレベルではなくゲームレベルに適用されます。これにより、合計リクエストレートがクォータを超えない限り、サーバー間でリクエストを柔軟に割り当てることができます。クォータを超えると、サービスがリクエストを制限したときにエラーレスポンスが返されます。
可観測性機能を使用すると、ゲームのリクエストユニットクォータをリアルタイムで確認できます。
データ構造サイズ制限
単一のソートマップまたはキューに対して、次のサイズおよびアイテム数の制限が適用されます:
- 最大アイテム数: 1,000,000
- 最大総サイズ(ソートマップのキーを含む): 100 MB
パーティションごとの制限
ゲームレベルのリクエストユニットクォータに加えて、メモリストアはすべてのゲームのサービスの安定性を保護するために各パーティションにリクエスト制限を適用します。これらの制限は、ゲームに割り当てられるクォータではなく、ゲームが達成できるスループットの上限でもありません。以下の値は推定値です。これらはRobloxのバックエンドで設定されており、変更される可能性があるため、固定値として設計しないでください。
すべてのリクエストは、データ構造に関係なく、アイテムを保持するパーティションの制限にカウントされます。現在、単一のパーティションに対して約30,000リクエストユニット/分で制限が始まることを期待できますので、そのレートを大幅に下回るようにしてください。データ構造が消費する制限の量は、アイテムの分布によって異なります:
- ソートマップとキューはそれぞれ単一のパーティションに存在するため、これらのデータ構造へのすべてのリクエストは同じパーティション制限にカウントされます。
- ハッシュマップはアイテムを多くのパーティションに分散させるため、多くのアイテムキーにわたってトラフィックが分散されると、いずれかのパーティションの制限に達することはほとんどありません。
ハッシュマップには、各アイテムキーに対して約5,000書き込みリクエストユニット/分および15,000読み取りリクエストユニット/分の追加制限があります。このキーごとの制限は、パーティションごとの制限の上に適用されるため、頻繁にアクセスされるキーはどちらかに達する可能性があります。キーごとの制限は、単一キー操作にのみ適用されます:読み取り制限は MemoryStoreHashMap:GetAsync() に適用され、書き込み制限は MemoryStoreHashMap:SetAsync() および MemoryStoreHashMap:RemoveAsync() に適用され、MemoryStoreHashMap:UpdateAsync() は読み取り制限と書き込み制限の両方にカウントされます。MemoryStoreHashMap:ListItemsAsync() はパーティションをスキャンするため、単一キーには適用されず、パーティションごとの制限のみが適用されます。

ソートや先入れ先出し機能が必要ない場合、ハッシュマップは通常、パーティション間で負荷を分散できるため、最良の選択です。
リクエストがパーティションまたはキーごとの制限を超えると、サービスはそれらを制限し、PartitionRequestsOverLimit ステータスコードを返します。これは、可観測性ダッシュボードで監視できます。
データ構造またはアイテムキーが高い持続的なリクエストレートを受けることが予想される場合は、シャーディングを行い、負荷をより多くのパーティションやキーに分散させます。読み取りと書き込みを複数のアイテムキーに均等に分散させて、キーごとの制限内に収めます。また、サーバー上に値をキャッシュし、一定の間隔で再確認し、可能な場合はリクエストをバッチ処理し、制限応答を受け取ったときに指数バックオフを適用することで、リクエストレートを減少させることもできます。
メモリクォータの枯渇からの回復
ゲームが TotalMemoryOverLimit エラーを受け取った場合は、可観測性ダッシュボードのメモリ使用量チャートを確認してください。メモリ使用量を増加させる書き込みを再開するには、不要なアイテムを削除するか、アイテムが期限切れになるのを待つか、次のオプションのいずれかを使用します。
Open Cloudでメモリストアをフラッシュ
保存されたデータがもはや必要ない場合は、Open CloudのFlush Memory Storeエンドポイントを使用して、選択したスコープのユニバース内のすべてのメモリストアデータ構造を非同期的にクリアします。
メモリストアをフラッシュするには:
- メモリストアをフラッシュし、ターゲットユニバースのフラッシュ操作のステータスを取得する権限を持つAPIキーを作成します。
- https://apis.roblox.com/cloud/v2/universes/{universe_id}/memory-store:flush に POST リクエストを送信し、APIキーを x-api-key ヘッダーに含めます。 {universe_id} をゲームのユニバースIDに置き換えます。 scope クエリパラメータを LIVE に設定して本番データを、TEST に設定してスタジオデータを指定します。 scope を省略すると、エンドポイントは LIVE を使用します。
- Get Memory Store Flush Operationを使用して、フラッシュリクエストによって返された操作をポーリングし、done が true になるまで待機し、error を確認します。ポーリング時には同じスコープを使用します。返された path にはすでに cloud/v2 が含まれているため、別のプレフィックスを追加せずに https://apis.roblox.com/{path} でポーリングします。
スタジオと本番のメモリストアは隔離されています。1つのスコープをフラッシュしても、他のスコープはクリアされません。不要なデータをクリアした後は、ベストプラクティスに従い、短い有効期限や明示的な削除を行って、メモリ使用量が再びクォータに達しないようにします。
拡張サービスで容量を増やす
ゲームがデータを保持し、デフォルトのクォータが許可する以上のストレージを使用する必要がある場合は、メモリストアのために拡張サービスを有効にします。拡張サービスを使用すると、デフォルトのクォータを超えるメモリストアのストレージとリクエストに対して支払うことができます。
始めるには、適格要件を満たし、アカウントの詳細と支払い方法を追加し、Creator Dashboardでゲームを選択します。拡張サービスの下で、メモリストアを有効にし、必要なリソースの月間予算を設定します。詳細については、サービスを有効にするおよびサービス料金を参照してください。
拡張サービスは、使用量が予算を超えるとサービスを制限します。使用量と請求を監視し、引き続き不要なデータを削除します。
ベストプラクティス
メモリ使用パターンを最適に保ち、制限に達しないようにするために、次のベストプラクティスに従ってください:
処理されたアイテムを削除します。 MemoryStoreQueue:RemoveAsync() メソッドを使用してキューの読み取ったアイテムを一貫してクリーンアップし、ソートマップには MemoryStoreSortedMap:RemoveAsync() を使用することで、メモリを解放し、データ構造を最新の状態に保つことができます。
データを追加する際には、可能な限り最小の時間枠に有効期限を設定します。 MemoryStoreQueue:AddAsync() と MemoryStoreSortedMap:SetAsync() のデフォルトの有効期限は45日ですが、最短の時間を設定することで古いデータを自動的にクリーンアップし、メモリ使用量クォータを埋め尽くすのを防ぐことができます。
- 長い有効期限で大量のデータを保存しないでください。これはメモリクォータを超えるリスクがあり、ゲーム全体を壊す可能性のある問題を引き起こす可能性があります。
- 常に不要なアイテムを明示的に削除するか、短いアイテムの有効期限を設定してください。
- 一般的に、メモリを解放するためには明示的な削除を使用し、アイテムの有効期限は未使用のアイテムが長期間メモリを占有しないようにするための安全機構として使用すべきです。
メモリに必要な値のみを保持します。
たとえば、オークションハウスゲームでは、最高入札額のみを維持する必要があります。すべての入札をデータ構造に保持するのではなく、1つのキーで MemoryStoreSortedMap:UpdateAsync() を使用して最高入札額を保持できます。
指数バックオフを使用してAPIリクエスト制限を下回るようにします。
たとえば、DataUpdateConflict を受け取った場合、2秒後に再試行し、その後4秒、8秒などとするのが良いでしょう。MemoryStoreService に対して正しい応答を得るためにリクエストを常に送信するのではなく。
巨大なデータ構造をシャーディングして複数の小さなものに分割します。
すべてを1つの大きなデータ構造に保存するよりも、小さな構造でデータを管理する方が簡単です。このアプローチは、使用量やレート制限を回避するのにも役立ちます。たとえば、キーにプレフィックスを使用するソートマップがある場合は、各プレフィックスを独自のソートマップに分けることを検討してください。特に人気のあるゲームの場合、ユーザーIDの最後の数字に基づいてユーザーを複数のマップに分けることさえ考えられます。
ハッシュマップの頻繁にアクセスされるキーを複数のキーのコピーでシャーディングします。
保存された値を圧縮します。
たとえば、保存された値のサイズを減らすためにLZWアルゴリズムを使用することを検討してください。
拡張サービスに登録します。
デフォルトのクォータを超えるストレージまたはリクエスト使用量については、メモリストアのために拡張サービスを有効にします。
可観測性
可観測性ダッシュボードは、メモリストアの使用状況を監視およびトラブルシューティングするための洞察と分析を提供します。メモリ使用量やAPIリクエストのさまざまな側面に関するリアルタイム更新チャートを使用して、ゲームのメモリ使用パターンを追跡し、現在の割り当てられたクォータを表示し、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を使用するには。 |
| InvalidClientAccess | MemoryStoreServiceはサーバーから呼び出す必要があります。 |
| InvalidExpirationTime | フィールド 'expiration' の時間は0から3,888,000の間でなければなりません。 |
| InvalidRequest | 値をjsonに変換できません。 |
| InvalidRequest | sortKeyを有効な数値または文字列に変換できません。 |
| TransformCallbackFailed | 変換コールバック関数の呼び出しに失敗しました。 |
| RequestThrottled | 最近のMemoryStoresリクエストが1つ以上の制限に達しました。 |
| UpdateConflict | 最大再試行回数を超えました。 |
トラブルシューティング
以下の表は、各レスポンスステータスコードに対する推奨される解決策をリストし、説明します。
| エラー | トラブルシューティングオプション |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit |
|
| DataUpdateConflict |
リクエストを中止する例 |
| 内部エラー |
|
| 無効なリクエスト |
|
| アイテム値サイズが大きすぎる |
|
スタジオでのテストとデバッグ
MemoryStoreService のデータはスタジオと本番の間で隔離されているため、スタジオでデータを変更しても本番の動作には影響しません。これにより、スタジオからのAPI呼び出しは本番データにアクセスせず、メモリストアや新機能を本番に移行する前に安全にテストできます。
スタジオテストには、本番と同じ制限とクォータが適用されます。ユーザー数に基づいて計算されるクォータの場合、スタジオテストではあなたが唯一のユーザーであるため、結果として得られるクォータは非常に小さくなる可能性があります。スタジオからテストする際には、アクセスと権限を確認するために実行される追加のチェックのために、本番での使用に比べてわずかに高い遅延とエラー率が見られることがあります。
ライブゲームやスタジオでのテスト中にメモリストアをデバッグする方法については、Developer Consoleを使用してください。