MemoryStoreService è un servizio di dati ad alta capacità e bassa latenza che fornisce un rapido storage di dati in memoria accessibile da tutti i server in una sessione live. Memorie memorizzate sono adatte per dati frequenti ed efimeri che cambiano rapidamente e non necessitano di essere durevoli, poiché sono più rapide da accedere e scompaiono al raggiungimento della durata massima. Per i dati che devono persistere tra le sessioni, utilizza data stores.
Strutture dati
Invece di accedere direttamente ai dati grezzi, le memorie memorizzate hanno tre strutture dati primitive condivise tra i server per un'elaborazione rapida: sorted map, queue, e hash map. Ogni struttura dati è adatta per determinati casi d'uso:
- Abbinamento basato sulle abilità - Salva le informazioni sugli utenti, come il livello di abilità, in una queue condivisa tra i server, e utilizza i server lobby per eseguire l'abbinamento periodicamente.
- Commercio e asta tra server - Abilita il commercio universale tra diversi server, dove gli utenti possono fare offerte su oggetti con prezzi che cambiano in tempo reale, utilizzando una sorted map di coppie chiave-valore.
- Classifiche globali - Memorizza e aggiorna le classifiche degli utenti in una classifica condivisa all'interno di una sorted map.
- Inventari condivisi - Salva gli oggetti e le statistiche dell'inventario in una hash map condivisa, dove gli utenti possono utilizzare gli oggetti dell'inventario contemporaneamente tra loro.
- Cache per dati persistenti - Sincronizza e copia i tuoi dati persistenti in un data store in una hash map di memoria che può fungere da cache e migliorare le prestazioni del tuo gioco.
In generale, se hai bisogno di accedere ai dati in base a una chiave specifica, utilizza una hash map. Se hai bisogno che quei dati siano ordinati, usa una sorted map. Se hai bisogno di elaborare i tuoi dati in un ordine specifico, usa una queue.
Limiti e quote
Per mantenere la scalabilità e le prestazioni del sistema, le memorie memorizzate hanno quote di utilizzo dei dati per la dimensione della memoria, le richieste API e la dimensione della struttura dati.
Le memorie memorizzate hanno una politica di espulsione basata sul tempo di scadenza, noto anche come tempo di vita (TTL). Gli elementi vengono espulsi dopo che scadono e la quota di memoria viene liberata per nuove voci. Quando raggiungi il limite di memoria, tutte le richieste di scrittura successive falliscono fino a quando gli elementi non scadono o non vengono eliminati manualmente.
Quota di dimensione della memoria
La quota di memoria limita la quantità totale di memoria che un gioco può consumare. Non è un valore fisso; cambia nel tempo a seconda del numero di utenti nel gioco secondo la formula 64KB + 1.2KB * [numero di utenti]. La quota si applica a livello di gioco invece che a livello di server.
Quando gli utenti entrano nel gioco, la quota di memoria aggiuntiva è disponibile immediatamente. Quando gli utenti lasciano il gioco, la quota non si riduce immediatamente. C'è un periodo di tracciabilità di otto giorni prima che la quota venga rivalutata a un valore inferiore.
Dopo che il tuo gioco raggiunge la quota di dimensione della memoria, eventuali richieste API che aumentano la dimensione della memoria si rifiutano sempre. Le richieste che diminuiscono o non cambiano la dimensione della memoria hanno ancora successo.
Con il dashboard di osservabilità, puoi visualizzare la quota di dimensione della memoria del tuo gioco in tempo reale utilizzando il grafico Utilizzo della memoria.
Limiti delle richieste API
Una quota di unità richiesta si applica a tutte le chiamate API di MemoryStoreService. Questa quota è 1000 + 120 * [numero di utenti concorrenti] unità richieste al minuto.
La maggior parte delle chiamate API consuma solo un'unità richiesta, con alcune eccezioni:
MemoryStoreSortedMap:GetRangeAsync()
Consuma unità in base al numero di elementi restituiti. Ad esempio, se questo metodo restituisce 10 elementi, la chiamata conta come 10 unità richieste. Se restituisce una risposta vuota, conta come un'unità richiesta.
Consuma unità in base al numero di elementi restituiti, proprio come MemoryStoreSortedMap:GetRangeAsync(), ma consuma un'unità aggiuntiva ogni due secondi mentre legge. Specifica il tempo massimo di lettura con il parametro waitTimeout.
MemoryStoreHashMap:UpdateAsync()
Consuma un minimo di due unità.
MemoryStoreHashMap:ListItemsAsync()
Consuma [numero di partizioni scansionate] + [elementi restituiti] unità.
La quota delle richieste si applica anche a livello di gioco invece che a livello di server. Questo fornisce flessibilità per allocare le richieste tra i server finché il tasso total di richieste non supera la quota. Se superi la quota, ricevi una risposta di errore quando il servizio limita le tue richieste.
Con la funzione osservabilità disponibile, puoi visualizzare la quota di unità richieste del tuo gioco in tempo reale.
Limiti di dimensione della struttura dati
Per una singola sorted map o queue, si applicano i seguenti limiti di dimensione e conteggio degli oggetti:
- Numero massimo di elementi: 1.000.000
- Dimensione totale massima (inclusi i chiavi per la sorted map): 100 MB
Limiti per partizione
Consulta limiti per partizione.
Migliori pratiche
Per mantenere il tuo utilizzo della memoria ottimale ed evitare di raggiungere i limiti, segui queste migliori pratiche:
Rimuovi gli elementi elaborati. Pulire costantemente gli oggetti letti utilizzando il metodo MemoryStoreQueue:RemoveAsync() per le queue e MemoryStoreSortedMap:RemoveAsync() per le sorted map può liberare memoria e mantenere la struttura dati aggiornata.
Imposta il tempo di scadenza al più piccolo intervallo di tempo possibile quando aggiungi dati. Anche se il tempo di scadenza predefinito è di 45 giorni per sia MemoryStoreQueue:AddAsync() che MemoryStoreSortedMap:SetAsync(), impostare il tempo più breve possibile può pulire automaticamente i dati vecchi per impedire che riempiano la tua quota di utilizzo della memoria.
- Non memorizzare una grande quantità di dati con una scadenza lunga, poiché c'è il rischio di superare la tua quota di memoria e potenzialmente causare problemi che possono rompere tutto il tuo gioco.
- Eliminare sempre esplicitamente oggetti non necessari o impostare una breve scadenza per gli oggetti.
- In generale, dovresti utilizzare l'eliminazione esplicita per liberare memoria e la scadenza degli oggetti come meccanismo di sicurezza per impedire agli oggetti non utilizzati di occupare memoria per un lungo periodo di tempo.
Mantieni in memoria solo i valori necessari.
Ad esempio, per un gioco di asta, devi solo mantenere la migliore offerta. Puoi utilizzare MemoryStoreSortedMap:UpdateAsync() su una chiave per mantenere la migliore offerta piuttosto che mantenere tutte le offerte nella tua struttura dati.
Usa backoff esponenziale per aiutare a stare sotto i limiti delle richieste API.
Ad esempio, se ricevi un DataUpdateConflict, potresti riprovare dopo due secondi, quindi quattro, otto, ecc. piuttosto che inviare continuamente richieste a MemoryStoreService per ottenere la risposta corretta.
Dividi le enormi strutture dati in più più piccole tramite sharding.
È spesso più facile gestire i dati in strutture più piccole piuttosto che memorizzare tutto in un'unica grande struttura dati. Questo approccio può aiutare anche a evitare limiti di utilizzo e tassi. Ad esempio, se hai una sorted map che usa prefissi per le sue chiavi, considera di separare ogni prefisso in una propria sorted map. Per un gioco particolarmente popolare, potresti persino separare gli utenti in più mappe in base alle ultime cifre dei loro ID utente.
Shard le chiavi frequentemente accedute nelle hash map con copie multiple della chiave per distribuire il carico.
Comprimi i valori memorizzati.
Ad esempio, considera di utilizzare l'algoritmo LZW per ridurre la dimensione del valore memorizzato.
Iscriviti ai Servizi Estesi.
Puoi aumentare le tue quote di Storage e Request Limit iscrivendoti ai Servizi Estesi.
Osservabilità
Il Dashboard di Osservabilità fornisce approfondimenti e analisi per monitorare e risolvere i problemi del tuo utilizzo della memoria memorizzata. Con grafici aggiornati in tempo reale su diversi aspetti del tuo utilizzo della memoria e delle richieste API, puoi tracciare il modello di utilizzo della memoria del tuo gioco, visualizzare le quote attualmente allocate, monitorare lo stato delle API e identificare potenziali problemi per l'ottimizzazione delle prestazioni.
La seguente tabella elenca e descrive tutti i codici di stato delle risposte API disponibili nei grafici Conteggio delle richieste per stato e Richieste per API x Stato del Dashboard di Osservabilità. Per ulteriori informazioni su come risolvere questi errori, consulta Risoluzione dei problemi. Per la quota o il limite specifico a cui si riferisce un errore, consulta Limiti e Quote.
| Codice di stato | Descrizione |
|---|---|
| Successo | Successo. |
| DataStructureMemoryOverLimit | Supera il limite di dimensione della memoria a livello di struttura dati (100MB). |
| DataUpdateConflict | Conflitto a causa di un aggiornamento concorrente. |
| AccessDenied | Non autorizzato ad accedere ai dati di gioco. Questa richiesta non consuma unità richieste né utilizza la quota. |
| InternalError | Errore interno. |
| InvalidRequest | La richiesta non contiene le informazioni richieste o ha informazioni malformate. |
| DataStructureItemsOverLimit | Supera il limite di conteggio degli elementi a livello di struttura dati (1M). |
| NoItemFound | Nessun elemento trovato in MemoryStoreQueue:ReadAsync() o MemoryStoreSortedMap:UpdateAsync(). ReadAsync() interroga ogni 2 secondi e restituisce questo codice di stato fino a quando non trova elementi nella coda. |
| DataStructureRequestsOverLimit | Supera il limite di unità di richiesta a livello di struttura dati (100.000 unità richieste al minuto). |
| PartitionRequestsOverLimit | Supera il limite di unità di richiesta per partizione. |
| TotalRequestsOverLimit | Supera il limite di unità di richiesta a livello di universo. |
| TotalMemoryOverLimit | Supera la quota di memoria a livello di universo. |
| ItemValueSizeTooLarge | La dimensione del valore supera il limite (32KB). |
La seguente tabella elenca i codici di stato dal lato client, che attualmente non sono disponibili nel Dashboard di Osservabilità.
| Codice di stato | Descrizione |
|---|---|
| InternalError | Errore interno. |
| UnpublishedPlace | Devi pubblicare questo luogo per utilizzare MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService deve essere chiamato dal server. |
| InvalidExpirationTime | Il campo 'expiration' deve essere compreso tra 0 e 3,888,000. |
| InvalidRequest | Impossibile convertire il valore in json. |
| InvalidRequest | Impossibile convertire sortKey in un numero o stringa valida. |
| TransformCallbackFailed | Impossibile invocare la funzione di callback di trasformazione. |
| RequestThrottled | Richieste recenti a MemoryStores hanno raggiunto uno o più limiti. |
| UpdateConflict | Superato il numero massimo di tentativi. |
Risoluzione dei problemi
La seguente tabella elenca e descrive le soluzioni raccomandate per ciascun codice di stato di risposta:
| Errore | Opzioni di risoluzione dei problemi |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
|
| Errore interno |
|
| Richiesta non valida |
|
| Dimensione del valore dell'elemento troppo grande |
|
Test e debug in Studio
I dati in MemoryStoreService sono isolati tra Studio e produzione, quindi cambiare i dati in Studio non influisce sul comportamento della produzione. Ciò significa che le tue chiamate API da Studio non accedono ai dati di produzione, consentendoti di testare in sicurezza le memorie memorizzate e nuove funzionalità prima di andare in produzione.
Il testing in Studio ha gli stessi limiti e quote della produzione. Per le quote calcolate in base al numero di utenti, la quota risultante può essere molto piccola poiché sei l'unico utente per il testing in Studio. Quando testate da Studio, potresti anche notare una latenza leggermente più alta e tassi di errore elevati rispetto all'uso in produzione a causa di ulteriori controlli che vengono eseguiti per verificare l'accesso e le autorizzazioni.
Per informazioni su come debug un memoria memorizzata nei giochi live o quando testi in studio, utilizza la Console Sviluppatore.