En fonction du type de structure de données, MemoryStoreService impose des limites sur la mémoire et le nombre d'éléments d'une structure de données. Toutes les structures de données sont également contraintes par une limite de demande globale par partition.
Chaque jeu Roblox dispose d'un tableau de bord d'observabilité de la mémoire, qui comprend un ensemble de graphiques que vous pouvez utiliser pour surveiller l'utilisation du magasin de mémoire.
Cartes triées et files d'attente
Les cartes triées et les files d'attente ont toutes deux des limites sur le nombre maximum d'éléments et la mémoire totale maximale. De plus, les éléments de l'une de ces structures de données résident toujours sur une seule partition. Chaque demande à l'une de ces structures de données est une demande vers la même partition.
Lorsque qu'une carte triée ou une file d'attente atteint sa limite d'éléments ou de mémoire, la meilleure chose à faire est d'éliminer manuellement les éléments inutiles ou d'ajouter une politique d'expiration pour les éléments. Alternativement, si seule la limite de mémoire cause un throttling, vous pouvez essayer de réduire la taille de vos éléments en supprimant les informations inutiles de vos clés et valeurs.
Si vous avez besoin de tous vos éléments ou si vous rencontrez un throttling en raison du débit des demandes, la seule solution est le sharding.
Sharding
Le sharding est le processus de stockage d'un ensemble de données connexes à travers plusieurs structures de données. En d'autres termes, cela signifie prendre une structure de données existante à fort débit et la remplacer par plusieurs plus petites qui contiennent ensemble le même ensemble de données que l'original.
Le principal défi du sharding est de trouver un moyen de répartir les données entre plusieurs structures de données tout en maintenant la même fonctionnalité que l'original.
Pour les tables de hachage, bien que la structure de données soit déjà partitionnée, le sharding se fait en répartissant les demandes entre plusieurs clés.
Sharding d'une carte triée
Pour sharder une carte triée, envisagez de diviser vos données en sous-sections alphabétiques avec des plages de caractères. Par exemple, supposons que vous n'ayez que des clés dont la première lettre va de A à Z, et que vous croyiez que quatre cartes triées suffisent pour votre cas d'utilisation actuel et votre croissance future :
- La première carte peut couvrir A-G, la seconde H-N, la troisième O-T et la quatrième U-Z.
- Pour insérer ou récupérer un élément, utilisez la carte appropriée en fonction du caractère initial de l'élément.
Sharding d'une carte triée
-- Initialiser le service MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Créer vos seaux de carte triée
local sm_AtoG = MemoryStoreService:GetSortedMap("AtoG")
local sm_HtoM = MemoryStoreService:GetSortedMap("HtoM")
local sm_NtoT = MemoryStoreService:GetSortedMap("NtoT")
local sm_UtoZ = MemoryStoreService:GetSortedMap("UtoZ")
-- Fonction d'assistance pour récupérer le seau correct à partir de la clé de l'élément
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
-- Initialiser les noms des joueurs avec une valeur par défaut de 0
for _, player in game:GetService("Players"):GetPlayers() do
local bucket = getSortedMapBucket(player)
bucket:SetAsync(player, 0, 600)
end
-- Récupérer la valeur d'un joueur
local player = "myPlayer"
local bucket = getSortedMapBucket(player)
local playerScore = bucket:GetAsync(player)
print(playerScore)
Sharding d'une file d'attente
Le sharding d'une file d'attente est plus délicat que le sharding d'une carte triée. Bien que vous souhaitiez répartir le débit des demandes entre plusieurs files d'attente, les ajouts, lectures et suppressions ne se produisent qu'à l'avant ou à l'arrière de la file d'attente.
Une solution consiste à utiliser une file d'attente tournante, ce qui signifie créer plusieurs files d'attente et faire des rotations entre elles lorsque vous ajoutez ou lisez un élément :
Créer plusieurs files d'attente et les ajouter à un tableau.
Créer deux pointeurs locaux. L'un représente la file d'attente dont vous souhaitez lire et supprimer des éléments. L'autre représente la file d'attente dans laquelle vous souhaitez ajouter des éléments :
- Pour les opérations de lecture, calculez le nombre d'éléments dont vous avez besoin dans chaque file d'attente, ainsi que où déplacer le pointeur de lecture.
- Pour les opérations de suppression, passez les identifiants de la lecture à chaque file d'attente.
- Pour les opérations d'ajout, ajoutez à la file d'attente au pointeur d'ajout et incrémentez le pointeur.
Sharding d'une file d'attente
-- Initialiser le service MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- Créer vos files d'attente
local q1 = MemoryStoreService:GetQueue("q1")
local q2 = MemoryStoreService:GetQueue("q2")
local q3 = MemoryStoreService:GetQueue("q3")
local q4 = MemoryStoreService:GetQueue("q4")
-- Mettre les files d'attente dans un tableau
local queueArr = { q1, q2, q3, q4 }
-- Créer deux pointeurs représentant les indices des files d'attente de lecture et d'ajout
local readIndex = 1
local addIndex = 1
-- Créer une fonction locale qui met à jour les indices de manière appropriée
local function rotateIndex(index, n)
return (index + n - 1) % 4 + 1
end
-- Créer une fonction locale qui lit n éléments de la file d'attente
local function readFromQueue(count, allOrNothing, waitTimeout)
local endIndex = count % 4
local countPerQueue = count // 4
local items = {}
local ids = {}
-- boucle à travers chaque file d'attente
for i = 1, 4, 1 do
-- déterminer si cette file d'attente lira un élément supplémentaire
local diff = i - readIndex
if diff < 0 then
diff += 4
end
local queue = queueArr[i]
-- lire des éléments de chaque file d'attente
-- +1 éléments si correspond aux critères de lecture supplémentaires
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
-- Créer une fonction locale qui supprime n éléments de la file d'attente
local function removeFromQueue(ids)
for i = 1, 4, 1 do
local queue = queueArr[readIndex]
queue:RemoveAsync(ids[i])
end
end
-- Créer une fonction locale qui ajoute un élément à la file d'attente
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[readIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- Écrire du code !
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)
Tables de hachage
Les tables de hachage n'ont pas de limites individuelles de mémoire ou de compte d'éléments et sont automatiquement shardées, mais vous pouvez toujours rencontrer un throttling si vous les utilisez mal.
Par exemple, envisagez un jeu avec une table de hachage de données, stockée comme valeur d'une seule clé nommée metadata. Si ces métadonnées contiennent un objet imbriqué avec des informations telles que l'ID de lieu, le nombre de joueurs, et plus encore, chaque fois que les métadonnées sont nécessaires, vous n'avez d'autre choix que d'appeler GetAsync("metadata") et de récupérer l'objet entier. Dans ce cas, toutes les demandes vont à une seule clé et donc à une seule partition.
Plutôt que de stocker toutes les métadonnées comme un seul objet imbriqué, la meilleure approche est de stocker chaque champ comme sa propre clé afin que la table de hachage puisse tirer parti du sharding automatique. Si vous avez besoin de séparation entre les métadonnées et le reste de la table de hachage, ajoutez un préfixe de nom (par exemple, metadata_user_count plutôt que simplement user_count).
De plus, si une ou quelques clés sont fréquemment consultées, il est important de shard ces appels entre de nombreuses clés. Par exemple, si tous les serveurs de jeu doivent récupérer une valeur à partir d'une clé de table de hachage, vous pouvez rencontrer un throttling de partition. Pour éviter cela, vous pouvez répartir ces appels entre plusieurs clés avec la même valeur jusqu'à ce que le throttling de partition ne se produise plus.