MemoryStoreService هي خدمة بيانات ذات تدفق عالي وزمن استجابة منخفض، توفر تخزين بيانات سريع في الذاكرة يمكن الوصول إليه من جميع الخوادم في جلسة حية. تخزين الذاكرة مناسب للبيانات المتكررة والعابرة التي تتغير بسرعة ولا تحتاج إلى أن تكون دائمة، لأنها أسرع في الوصول وتختفي عند الوصول إلى الحد الأقصى لعمرها. للبيانات التي تحتاج إلى الاستمرار عبر الجلسات، استخدم مخازن البيانات.
هياكل البيانات
بدلاً من الوصول المباشر إلى البيانات الخام، تمتلك تخزين الذاكرة ثلاث هياكل بيانات أولية مشتركة بين الخوادم لمعالجة سريعة: خريطة مرتبة، قائمة انتظار، و خريطة تجزئة. كل هيكل بيانات مناسب لحالات استخدام معينة:
- المطابقة المعتمدة على المهارة - إحفظ معلومات المستخدم، مثل مستوى المهارة، في قائمة انتظار مشتركة بين الخوادم، واستخدم خوادم اللوبي لتنفيذ المطابقة بشكل دوري.
- التجارة والمزادات عبر الخوادم - إتاحة التجارة العامة بين الخوادم المختلفة، حيث يمكن للمستخدمين المزايدة على العناصر مع أسعار تتغير في الوقت الفعلي، مع خريطة مرتبة من أزواج المفتاح-القيمة.
- لوحات القيادة العالمية - تخزين وتحديث تصنيفات المستخدمين على لوحة قيادة مشتركة داخل خريطة مرتبة.
- المخازن المشتركة - إحفظ عناصر المخزون والإحصائيات في خريطة تجزئة مشتركة، حيث يمكن للمستخدمين استخدام عناصر المخزون مع بعضهم بشكل متزامن.
- ذاكرة تخزين للبيانات الدائمة - مزامنة ونسخ بياناتك المستمرة في مخزن بيانات إلى خريطة تجزئة في ذاكرة التخزين تعمل كذاكرة مؤقتة وتحسن من أداء لعبتك.
بشكل عام، إذا كنت بحاجة إلى الوصول إلى البيانات بناءً على مفتاح معين، استخدم خريطة التجزئة. إذا كنت بحاجة إلى أن تكون هذه البيانات مرتبة، استخدم خريطة مرتبة. إذا كنت بحاجة إلى معالجة بياناتك بترتيب معين، استخدم قائمة الانتظار.
الحدود والكوطة
للإبقاء على قابلية التوسع وأداء النظام، تمتلك تخزين الذاكرة حدود استخدام البيانات الخاصة بحجم الذاكرة، طلبات API، وحجم هيكل البيانات.
تمتلك تخزين الذاكرة سياسة طرد تعتمد على وقت انتهاء الصلاحية، والمعروفة أيضًا باسم "الوقت للحياة" (TTL). يتم طرد العناصر بعد انتهاء صلاحيتها، ويتم تحرير حصة الذاكرة لإدخالات جديدة. عندما تصل إلى حد الذاكرة، تفشل جميع طلبات الكتابة اللاحقة حتى تنتهي صلاحية العناصر أو تقوم بحذفها يدويًا.
حصة حجم الذاكرة
تحدد حصة الذاكرة إجمالي كمية الذاكرة التي يمكن أن تستهلكها اللعبة. ليست قيمة ثابتة؛ بل تتغير بمرور الوقت حسب عدد المستخدمين في اللعبة وفقًا للصيغة 64KB + 1.2KB * [عدد المستخدمين]. تنطبق الحصة على مستوى اللعبة بدلاً من مستوى الخادم.
عندما ينضم المستخدمون إلى اللعبة، تتوفر حصة الذاكرة الإضافية على الفور. عندما يغادر المستخدمون اللعبة، لا تتقليص الحصة على الفور. هناك فترة زمنية للخلف تمتد لثمانية أيام قبل أن يتم إعادة تقييم الحصة إلى قيمة أقل.
بعد أن تصل لعبتك إلى حصة حجم الذاكرة، تفشل أي طلبات API تزيد من حجم الذاكرة دائمًا. الطلبات التي تقلل أو لا تغير من حجم الذاكرة لا تزال تنجح.
مع لوحة الرصد، يمكنك مشاهدة حصة حجم الذاكرة للعبتك في الوقت الحقيقي باستخدام مخطط استخدام الذاكرة.
حدود طلبات API
تنطبق حصة وحدات الطلب على جميع استدعاءات MemoryStoreService API. هذه الحصة هي 1000 + 120 * [عدد المستخدمين المتزامنين] من وحدات الطلب في الدقيقة.
معظم استدعاءات API تستهلك وحدة طلب واحدة فقط، مع بعض الاستثناءات:
MemoryStoreSortedMap:GetRangeAsync()
تستهلك وحدات بناءً على عدد العناصر المعادة. على سبيل المثال، إذا أعادت هذه الطريقة 10 عناصر، فإن الاستدعاء يعد كـ 10 وحدات طلب. إذا أعادت استجابة فارغة، فسيحسب كـ وحدة طلب واحدة.
تستهلك وحدات بناءً على عدد العناصر المعادة، تمامًا مثل MemoryStoreSortedMap:GetRangeAsync()، لكن تستهلك وحدة إضافية كل ثانيتين أثناء القراءة. حدد الحد الأقصى لوقت القراءة باستخدام معلمة waitTimeout.
MemoryStoreHashMap:UpdateAsync()
تستهلك حد أدنى من وحدتين.
MemoryStoreHashMap:ListItemsAsync()
تستهلك [عدد الأقسام الممسوحة] + [العناصر المعادة] وحدات.
يتم تطبيق حصة الطلبات أيضًا على مستوى اللعبة بدلاً من مستوى الخادم. يوفر ذلك مرونة لتوزيع الطلبات بين الخوادم طالما أن المعدل الإجمالي للطلبات لا يتجاوز الحصة. إذا تجاوزت الحصة، ستحصل على رد خطأ عندما تقوم الخدمة بخنق طلباتك.
مع ميزة الرصد المتاحة، يمكنك عرض حصة وحدة الطلبات للعبتك في الوقت الحقيقي.
حدود حجم هيكل البيانات
بالنسبة لخريطة مرتبة واحدة أو قائمة انتظار، تنطبق الحدود التالية على الحجم وعدد العناصر:
- الحد الأقصى لعدد العناصر: 1,000,000
- الحد الأقصى للإجمالي الحجم (بما في ذلك المفاتيح لخريطة مرتبة): 100 ميجابايت
حدود لكل قسم
راجع الحدود لكل قسم.
أفضل الممارسات
للحفاظ على نمط استخدام الذاكرة الأمثل وتجنب الوصول إلى الحدود، اتبع هذه الممارسات الجيدة:
قم بإزالة العناصر المعالجة. يعمل تنظيف العناصر المقروءة بانتظام باستخدام MemoryStoreQueue:RemoveAsync() لقوائم الانتظار و MemoryStoreSortedMap:RemoveAsync() لخرايط مرتبة على تحرير الذاكرة وإبقاء هيكل البيانات محدثًا.
حدد وقت انتهاء الصلاحية لأقصر فترة زمنية ممكنة عند إضافة البيانات. على الرغم من أن وقت انتهاء الصلاحية الافتراضي هو 45 يومًا لكل من MemoryStoreQueue:AddAsync() و MemoryStoreSortedMap:SetAsync()، فإن تحديد أقصر وقت ممكن يمكن أن يؤدي تلقائيًا إلى تنظيف البيانات القديمة لمنعها من ملء حصة استخدام الذاكرة الخاصة بك.
- لا تخزن كمية كبيرة من البيانات مع انتهاء صلاحية طويلة، لأنها تعرض خطتك لتجاوز الحصة الخاصة بك وتسبب مشكلات يمكن أن تعطل لعبتك بالكامل.
- دائمًا قم إما بحذف العناصر غير الضرورية صراحة أو تعيين انتهاء صلاحية قصير للعناصر.
- بشكل عام، يجب عليك استخدام الحذف الصريح لإطلاق الذاكرة وانتهاء صلاحية العنصر كآلية أمان لمنع العناصر غير المستخدمة من شغل الذاكرة لفترة طويلة.
احتفظ فقط بالقيم الضرورية في الذاكرة.
على سبيل المثال، بالنسبة للعبة مزاد، تحتاج فقط للحفاظ على أعلى مزايدة. يمكنك استخدام MemoryStoreSortedMap:UpdateAsync() على مفتاح واحد للحفاظ على أعلى مزايدة بدلاً من الاحتفاظ بجميع المزايدات في هيكل البيانات الخاص بك.
استخدم التراجع الأسي للمساعدة في البقاء تحت حدود طلبات API.
على سبيل المثال، إذا تلقيت DataUpdateConflict، يمكنك إعادة المحاولة بعد ثانيتين، ثم أربع، ثماني، إلخ. بدلًا من إرسال الطلبات بشكل متواصل إلى MemoryStoreService للحصول على الاستجابة الصحيحة.
قم بتقسيم هياكل البيانات الضخمة إلى هياكل أصغر متعددة عن طريق تجزئتها.
من الأسهل غالبًا إدارة البيانات في هياكل أصغر بدلًا من تخزين كل شيء في هيكل بيانات كبير واحد. يمكن أن يساعد هذا النهج أيضًا في تجنب حدود الاستخدام والمعدل. على سبيل المثال، إذا كان لديك خريطة مرتبة تستخدم بادئات لمفاتيحها، فكر في فصل كل بادئة إلى خريطتها المرتبة الخاصة. بالنسبة للعبة شعبية بشكل خاص، قد تفصل حتى المستخدمين إلى خرائط متعددة بناءً على الأرقام الأخيرة من معرفات المستخدمين الخاصة بهم.
قم بتجزئة المفاتيح التي يتم الوصول إليها بشكل متكرر في خرائط التجزئة مع نسخ متعددة من المفتاح لتوزيع الحمل.
قم بضغط القيم المخزنة.
على سبيل المثال، فكر في استخدام خوارزمية LZW لتقليل حجم القيمة المخزنة.
انضم إلى خدمات موسعة.
يمكنك زيادة حدود التخزين وحدود الطلبات الخاصة بك من خلال الانخراط في الخدمات الموسعة.
الرصد
توفر لوحة الرصد رؤى وتحليلات لمراقبة وحل مشكلات استخدام تخزين الذاكرة الخاص بك. مع المخططات التي يتم تحديثها في الوقت الحقيقي حول جوانب مختلفة من استخدام الذاكرة وطلبات API، يمكنك تتبع نمط استخدام الذاكرة للعبتك، عرض الحصص المخصصة الحالية، مراقبة حالة API، وتحديد المشكلات المحتملة لتحسين الأداء.
تسرد الجدول التالي وتصف جميع رموز الحالة لاستجابات API المتاحة على مخططات عدد الطلبات حسب الحالة و طلبات حسب API x الحالة في لوحة الرصد. لمزيد من المعلومات حول كيفية حل هذه الأخطاء، راجع استكشاف الأخطاء وإصلاحها. للحد أو الحصة المحددان الذي يتعلق به خطأ، راجع الحدود والكوطة.
| رمز الحالة | الوصف |
|---|---|
| نجاح | نجاح. |
| DataStructureMemoryOverLimit | يتجاوز حد حجم الذاكرة لمستوى هيكل البيانات (100 ميجابايت). |
| DataUpdateConflict | تعارض بسبب التحديث المتزامن. |
| AccessDenied | غير مصرح بالوصول إلى بيانات اللعبة. هذا الطلب لا يستهلك وحدات الطلب أو يستخدم الحصة. |
| InternalError | خطأ داخلي. |
| InvalidRequest | الطلب ليس لديه المعلومات المطلوبة أو يحتوي على معلومات مشوهة. |
| DataStructureItemsOverLimit | يتجاوز حد عدد العناصر لمستوى هيكل البيانات (1M). |
| NoItemFound | لم يتم العثور على عنصر في MemoryStoreQueue:ReadAsync() أو MemoryStoreSortedMap:UpdateAsync(). ReadAsync() يستعلم كل ثانيتين ويعيد هذا رمز الحالة حتى يجد عناصر في قائمة الانتظار. |
| DataStructureRequestsOverLimit | يتجاوز حد وحدات الطلب لمستوى هيكل البيانات (100,000 وحدة طلب في الدقيقة). |
| PartitionRequestsOverLimit | يتجاوز حد وحدات الطلب الخاصة بالقسم. |
| TotalRequestsOverLimit | يتجاوز حد وحدات الطلب على مستوى الكون. |
| TotalMemoryOverLimit | يتجاوز حد الذاكرة على مستوى الكون. |
| ItemValueSizeTooLarge | حجم القيمة يتجاوز الحد (32KB). |
تسرد الجدول التالي رموز الحالة من جانب العميل، والتي غير متاحة حاليًا على لوحة الرصد.
| رمز الحالة | الوصف |
|---|---|
| InternalError | خطأ داخلي. |
| UnpublishedPlace | يجب عليك نشر هذا المكان لاستخدام MemoryStoreService. |
| InvalidClientAccess | يجب استدعاء MemoryStoreService من الخادم. |
| InvalidExpirationTime | يجب أن يكون حقل 'expiration' بين 0 و 3,888,000. |
| InvalidRequest | غير قادر على تحويل القيمة إلى JSON. |
| InvalidRequest | غير قادر على تحويل sortKey إلى رقم أو سلسلة صالحين. |
| TransformCallbackFailed | فشل في استدعاء دالة الرجوع للتحول. |
| RequestThrottled | طلبات MemoryStores الأخيرة تصطدم بواحد أو أكثر من الحدود. |
| UpdateConflict | تجاوز الحد الأقصى لعدد المحاولات. |
استكشاف الأخطاء وإصلاحها
تسرد الجدول التالية وتصف الحلول الموصى بها لكل رمز حالة رد:
| خطأ | خيارات استكشاف الأخطاء وإصلاحها |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
|
| خطأ داخلي |
|
| طلب غير صالح |
|
| حجم قيمة العنصر كبير جدًا |
|
اختبار وتصحيح في الاستوديو
البيانات في MemoryStoreService معزولة بين الاستوديو والإنتاج، لذا فإن تغيير البيانات في الاستوديو لا يؤثر على سلوك الإنتاج. هذا يعني أن استدعاءات API الخاصة بك من الاستوديو لا تصل إلى بيانات الإنتاج، مما يسمح لك باختبار تخزين الذاكرة والميزات الجديدة بأمان قبل الانتقال إلى الإنتاج.
يخبر الاستوديو بنفس الحدود والكوطة مثل الإنتاج. بالنسبة للحصص المحسوبة استنادًا إلى عدد المستخدمين، يمكن أن تكون الحصة الناتجة صغيرة جدًا نظرًا لأنك المستخدم الوحيد خلال اختبار الاستوديو. عند الاختبار من الاستوديو، قد تلاحظ أيضًا زمن استجابة أعلى قليلاً ومعدلات خطأ مرتفعة مقارنة بالاستخدام في الإنتاج بسبب بعض الفحوصات الإضافية التي يتم إجراؤها للتحقق من الوصول والإذن.
لمزيد من المعلومات حول كيفية تصحيح تخزين الذاكرة في الألعاب الحية أو عند الاختبار في الاستوديو، استخدم وحدة التحكم للمطورين.