اعتمادًا على نوع هيكل البيانات، يفرض MemoryStoreService حدودًا على الذاكرة وعدد العناصر في هيكل البيانات. كما أن جميع هياكل البيانات مقيدة أيضًا بحد طلب عالمي لكل قسم.
يمتلك كل لعبة من ألعاب Roblox لوحة مراقبة استخدام ذاكرة التخزين، والتي تتضمن مجموعة من الرسوم البيانية التي يمكنك استخدامها لمراقبة استخدام ذاكرة التخزين.
الخرائط المرتبة والصفوف
تحتوي الخرائط المرتبة والصفوف على حدود لعدد العناصر الأقصى والإجمالي الأقصى للذاكرة. بالإضافة إلى ذلك، تكون العناصر في أحد هذه الهياكل دائمًا في قسم واحد. كل طلب إلى أحد تلك الهياكل هو طلب إلى نفس القسم.
عندما تصل الخريطة المرتبة أو الصف إلى حد عدد العناصر أو الذاكرة، فإن أفضل إجراء هو إزالة العناصر غير الضرورية يدويًا أو من خلال إضافة سياسة انتهاء صلاحية للعناصر. بدلاً من ذلك، إذا كان حد الذاكرة فقط هو ما يسبب الحد من الاستخدام، يمكنك محاولة تقليل حجم العناصر الخاصة بك عن طريق إزالة المعلومات غير الضرورية من المفاتيح والقيم.
إذا كنت بحاجة إلى جميع عناصرك أو كنت تعاني من الحد من الاستخدام بسبب كمية الطلبات، فإن الحل الوحيد هو تقسيم البيانات.
تقسيم البيانات
تقسيم البيانات هو عملية تخزين مجموعة من البيانات ذات الصلة عبر هياكل بيانات متعددة. بعبارة أخرى، يعني ذلك أخذ هيكل بيانات قائم وعالي الإنتاجية واستبداله بعدة هياكل أصغر تحتوي معًا على نفس مجموعة البيانات كما في الأصل.
التحدي الرئيسي في تقسيم البيانات هو إيجاد طريقة لنشر البيانات عبر عدة هياكل بيانات بطريقة تحافظ على نفس الوظائف كما في الأصل.
بالنسبة للخرائط الهاش، على الرغم من أن هيكل البيانات مقسم بالفعل، يتم تقسيم الطلبات من خلال نشرها عبر عدة مفاتيح.
تقسيم خريطة مرتبة
لتقسيم خريطة مرتبة، اعتبر تقسيم بياناتك إلى أقسام حسب الحروف مع نطاقات الأحرف. على سبيل المثال، افترض أن لديك فقط مفاتيح تبدأ من A إلى Z، وأنك تعتقد أن أربع خرائط مرتبة كافية للاستخدام الحالي ونمو المستقبل:
- يمكن أن تغطي الخريطة الأولى A-G، والثانية H-N، والثالثة O-T، والرابعة U-Z.
- لإدراج عنصر أو استرداده، استخدم الخريطة المناسبة بناءً على الحرف الأول من العنصر.
تقسيم خريطة مرتبة
-- تهيئة خدمة MemoryStore
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)
تقسيم الصف
تقسيم الصف أكثر تعقيدًا من تقسيم الخريطة المرتبة. على الرغم من أنك تريد نشر كمية الطلبات عبر عدة صفوف، إلا أن الإضافات والقراءات والإزالات تحدث دائمًا عند مقدمة أو نهاية الصف.
تتمثل إحدى الحلول في استخدام صف دوار، مما يعني إنشاء عدة صفوف والتناوب بينها عند إضافة أو قراءة عنصر:
إنشاء عدة صفوف وإضافتها إلى مصفوفة.
إنشاء مؤشرين محليين. يمثل أحدهما الصف الذي ترغب في قراءة وإزالة العناصر منه. بينما يمثل الآخر الصف الذي ترغب في إضافة العناصر إليه:
- لعمليات القراءة، احسب عدد العناصر التي تحتاجها من كل صف، بالإضافة إلى مكان تحريك المؤشر للقراءة.
- لعمليات الإزالة، مرر المعرفات من القراءة إلى كل صف.
- لعمليات الإضافة، أضف إلى الصف عند مؤشر الإضافة وزد المؤشر.
تقسيم صف
-- تهيئة خدمة MemoryStore
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 }
-- أنشئ مؤشرين يمثلان مؤشرات الصفوف للقراءة والإضافة
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. إذا كانت هذه البيانات الوصفية تحتوي على كائن متداخل يتضمن معلومات مثل معرف المكان، وعدد اللاعبين، وأكثر من ذلك، في كل مرة تحتاج فيها إلى البيانات الوصفية، ليس لديك خيار سوى استدعاء GetAsync("metadata") واسترداد الكائن بالكامل. في هذه الحالة، تذهب جميع الطلبات إلى مفتاح واحد وبالتالي إلى قسم واحد.
بدلاً من تخزين جميع البيانات الوصفية ككائن متداخل واحد، فإن النهج الأفضل هو تخزين كل حقل كمفتاحه الخاص حتى يمكن أن تستفيد خريطة الهاش من التجزئة التلقائية. إذا كنت بحاجة إلى فصل بين البيانات الوصفية وبقية خريطة الهاش، أضف بادئة تسمية (مثلاً metadata_user_count بدلاً من user_count).
بالإضافة إلى ذلك، إذا تم الوصول إلى مفتاح واحد أو عدد قليل من المفاتيح بشكل متكرر، فمن المهم تجزئة هذه الطلبات عبر الكثير من المفاتيح. على سبيل المثال، إذا كان يجب على جميع خوادم اللعبة استرداد قيمة من مفتاح خريطة هاش واحد، قد تواجه حدًا من الاستخدام للقسم. لمنع هذا، يمكنك نشر هذه الطلبات عبر عدة مفاتيح بنفس القيمة حتى لا يحدث حد للقسم مرة أخرى.