MemoryStoreService เป็นบริการข้อมูลที่มีความสามารถในการประมวลผลสูงและมีความหน่วงต่ำ ซึ่งให้การจัดเก็บข้อมูลในหน่วยความจำที่รวดเร็วและสามารถเข้าถึงได้จากเซิร์ฟเวอร์ทั้งหมดในเซสชันที่ใช้งานอยู่ Memory Stores เหมาะสำหรับข้อมูลที่มีการเปลี่ยนแปลงบ่อยและชั่วคราวซึ่งไม่จำเป็นต้องมีความทนทาน เนื่องจากเข้าถึงได้เร็วกว่าและจะหายไปเมื่อถึงอายุสูงสุด สำหรับข้อมูลที่ต้องการให้คงอยู่ระหว่างเซสชัน ให้ใช้ data stores.
โครงสร้างข้อมูล
แทนที่จะเข้าถึงข้อมูลดิบโดยตรง หน่วยความจำจัดเก็บมีโครงสร้างข้อมูลพื้นฐานสามประเภทที่แชร์กันระหว่างเซิร์ฟเวอร์เพื่อการประมวลผลที่รวดเร็ว: sorted map, queue, และ hash map. โครงสร้างข้อมูลแต่ละประเภทเหมาะสำหรับกรณีการใช้งานที่เฉพาะเจาะจง:
- การจับคู่ตามทักษะ - บันทึกข้อมูลผู้ใช้ เช่น ระดับทักษะ ใน queue ที่แชร์กันระหว่างเซิร์ฟเวอร์ และใช้เซิร์ฟเวอร์ล็อบบี้เพื่อทำการจับคู่เป็นระยะๆ
- การซื้อขายและการประมูลข้ามเซิร์ฟเวอร์ - เปิดใช้งานการซื้อขายทั่วไประหว่างเซิร์ฟเวอร์ที่แตกต่างกัน ซึ่งผู้ใช้สามารถเสนอราคาสำหรับรายการที่มีราคาเปลี่ยนแปลงแบบเรียลไทม์ โดยใช้ sorted map ของคู่คีย์-ค่า
- กระดานผู้นำระดับโลก - เก็บและอัปเดตอันดับผู้ใช้ในกระดานผู้นำที่แชร์กันภายใน sorted map
- สินค้าคงคลังที่แชร์กัน - บันทึกสินค้าคงคลังและสถิติใน hash map ที่แชร์กัน ซึ่งผู้ใช้สามารถใช้สินค้าคงคลังร่วมกันได้
- แคชสำหรับข้อมูลที่คงอยู่ - ซิงค์และคัดลอกข้อมูลที่คงอยู่ใน data store ไปยัง hash map ของหน่วยความจำที่สามารถทำหน้าที่เป็นแคชและปรับปรุงประสิทธิภาพของเกมของคุณ
โดยทั่วไป หากคุณต้องการเข้าถึงข้อมูลตามคีย์เฉพาะ ให้ใช้ hash map หากคุณต้องการให้ข้อมูลนั้นมีการจัดเรียง ให้ใช้ sorted map หากคุณต้องการประมวลผลข้อมูลของคุณในลำดับเฉพาะ ให้ใช้ queue
ข้อจำกัดและโควต้า
เพื่อรักษาความสามารถในการปรับขนาดและประสิทธิภาพของระบบ หน่วยความจำจัดเก็บมีโควต้าการใช้งานข้อมูลสำหรับขนาดหน่วยความจำ, การเรียก API, และขนาดของโครงสร้างข้อมูล
หน่วยความจำจัดเก็บมีนโยบายการลบตามเวลาหมดอายุ ซึ่งเรียกอีกอย่างว่าเวลาในการมีชีวิต (TTL) รายการจะถูกลบหลังจากหมดอายุ และโควต้าหน่วยความจำจะถูกปล่อยให้ว่างสำหรับรายการใหม่ เมื่อคุณถึงขีดจำกัดหน่วยความจำ การเขียนคำขอทั้งหมดที่ตามมาจะล้มเหลวจนกว่ารายการจะหมดอายุหรือคุณลบรายการเหล่านั้นด้วยตนเอง
โควต้าขนาดหน่วยความจำ
โควต้าหน่วยความจำจำกัดปริมาณหน่วยความจำทั้งหมดที่เกมสามารถใช้ได้ มันไม่ใช่ค่าคงที่ แต่จะเปลี่ยนแปลงไปตามจำนวนผู้ใช้ในเกมตามสูตร 64 KB + 1.2 KB * [number of users] โควต้าจะใช้ในระดับเกมแทนที่จะเป็นระดับเซิร์ฟเวอร์
เมื่อผู้ใช้เข้าร่วมเกม โควต้าหน่วยความจำเพิ่มเติมจะพร้อมใช้งานทันที เมื่อผู้ใช้ออกจากเกม โควต้าจะไม่ลดลงทันที จะมีระยะเวลาการติดตามเป็นเวลาแปดวันก่อนที่โควต้าจะประเมินค่าใหม่เป็นค่าที่ต่ำกว่า
หลังจากเกมของคุณถึงโควต้าขนาดหน่วยความจำ คำขอ API ใดๆ ที่เพิ่มขนาดหน่วยความจำจะล้มเหลวเสมอ คำขอที่ลดหรือไม่เปลี่ยนแปลงขนาดหน่วยความจำยังคงสำเร็จ
ด้วย observability แดชบอร์ด คุณสามารถดูโควต้าขนาดหน่วยความจำของเกมของคุณแบบเรียลไทม์โดยใช้แผนภูมิ Memory Usage
ขีดจำกัดการเรียก API
โควต้า request unit จะใช้กับการเรียก API ของ MemoryStoreService ทุกครั้ง โควต้านี้คือ 1000 + 120 * [number of concurrent users] request units ต่อหนึ่งนาที
การเรียก API ส่วนใหญ่จะใช้เพียงหนึ่ง request unit โดยมีข้อยกเว้นบางประการ:
MemoryStoreSortedMap:GetRangeAsync()
ใช้หน่วยตามจำนวนรายการที่ส่งคืน ตัวอย่างเช่น หากวิธีนี้ส่งคืน 10 รายการ การเรียกจะนับเป็น 10 request units หากส่งคืนการตอบสนองที่ว่างเปล่า จะนับเป็นหนึ่ง request unit
ใช้หน่วยตามจำนวนรายการที่ส่งคืน เช่นเดียวกับ MemoryStoreSortedMap:GetRangeAsync() แต่จะใช้หน่วยเพิ่มเติมทุกสองวินาทีในขณะที่อ่าน กำหนดเวลาการอ่านสูงสุดด้วยพารามิเตอร์ waitTimeout
MemoryStoreHashMap:UpdateAsync()
ใช้หน่วยขั้นต่ำสองหน่วย
MemoryStoreHashMap:ListItemsAsync()
ใช้ [number of partitions scanned] + [items returned] units
โควต้าคำขอยังใช้ในระดับเกมแทนที่จะเป็นระดับเซิร์ฟเวอร์ ซึ่งให้ความยืดหยุ่นในการจัดสรรคำขอระหว่างเซิร์ฟเวอร์ตราบใดที่อัตราคำขอรวมไม่เกินโควต้า หากคุณเกินโควต้า คุณจะได้รับการตอบสนองข้อผิดพลาดเมื่อบริการจำกัดคำขอของคุณ
ด้วยฟีเจอร์ observability ที่มีอยู่ คุณสามารถดูโควต้า request unit ของเกมของคุณแบบเรียลไทม์
ขีดจำกัดขนาดโครงสร้างข้อมูล
สำหรับ sorted map หรือ queue เดียว ขีดจำกัดขนาดและจำนวนรายการต่อไปนี้จะใช้:
- จำนวนรายการสูงสุด: 1,000,000
- ขนาดรวมสูงสุด (รวมถึงคีย์สำหรับ sorted map): 100 MB
ขีดจำกัดต่อพาร์ติชัน
นอกเหนือจาก request unit quota ในระดับเกมแล้ว หน่วยความจำจัดเก็บยังใช้ขีดจำกัดการเรียกต่อแต่ละพาร์ติชันเป็นมาตรการป้องกันที่ปกป้องความเสถียรของบริการสำหรับเกมทั้งหมด ขีดจำกัดเหล่านี้ไม่ใช่โควต้าที่เกมของคุณได้รับ และไม่ใช่เพดานที่แน่นอนสำหรับอัตราการส่งข้อมูลที่เกมของคุณสามารถทำได้ ค่าต่อไปนี้เป็นการประมาณการ พวกมันถูกกำหนดค่าในแบ็กเอนด์ของ Roblox และอาจเปลี่ยนแปลงได้ ดังนั้นอย่าออกแบบตามค่าคงที่เหล่านี้
ทุกคำขอนับเป็นส่วนหนึ่งของขีดจำกัดของ พาร์ติชัน ที่ถือรายการ ไม่ว่าจะเป็นโครงสร้างข้อมูลใดก็ตาม คุณสามารถคาดหวังว่าการจำกัดจะเริ่มต้นที่ประมาณ 30,000 request units ต่อหนึ่งนาที สำหรับพาร์ติชันเดียว ดังนั้นควรพยายามอยู่ต่ำกว่าระดับนั้น ขนาดของขีดจำกัดที่โครงสร้างข้อมูลใช้ขึ้นอยู่กับการกระจายของรายการ:
- Sorted maps และ queues จะอยู่ในพาร์ติชันเดียว ดังนั้นทุกคำขอไปยังโครงสร้างข้อมูลเหล่านี้จะนับเป็นขีดจำกัดของพาร์ติชันเดียวกัน
- Hash maps จะกระจายรายการของพวกเขาไปยังหลายพาร์ติชัน ดังนั้นการจราจรที่กระจายไปยังหลายคีย์รายการจึงไม่ค่อยเข้าใกล้ขีดจำกัดของพาร์ติชันใดพาร์ติชันหนึ่ง
Hash maps มีขีดจำกัดเพิ่มเติมในแต่ละคีย์รายการประมาณ 5,000 write request units ต่อหนึ่งนาที และ 15,000 read request units ต่อหนึ่งนาที ขีดจำกัดต่อคีย์นี้ใช้เพิ่มเติมจากขีดจำกัดต่อพาร์ติชัน ดังนั้นคีย์ที่เข้าถึงบ่อยอาจโดนทั้งสองขีดจำกัด ขีดจำกัดต่อคีย์ใช้เฉพาะกับการดำเนินการแบบคีย์เดียว: ขีดจำกัดการอ่านใช้กับ MemoryStoreHashMap:GetAsync(), ขีดจำกัดการเขียนใช้กับ MemoryStoreHashMap:SetAsync() และ MemoryStoreHashMap:RemoveAsync(), และ MemoryStoreHashMap:UpdateAsync() จะนับทั้งขีดจำกัดการอ่านและการเขียน MemoryStoreHashMap:ListItemsAsync() สแกนพาร์ติชันแทนที่จะเป็นคีย์เดียว ดังนั้นจะใช้เฉพาะขีดจำกัดต่อพาร์ติชันกับมัน

หากคุณไม่ต้องการการจัดเรียงหรือฟังก์ชันการทำงานแบบเข้ามาก่อนออกไป (first-in, first-out) โดยทั่วไป hash map จะเป็นตัวเลือกที่ดีที่สุดเพราะสามารถกระจายโหลดไปยังพาร์ติชันต่างๆ
เมื่อคำขอเกินขีดจำกัดของพาร์ติชันหรือขีดจำกัดต่อคีย์ บริการจะจำกัดคำขอเหล่านั้นและส่งคืนรหัสสถานะ PartitionRequestsOverLimit ซึ่งคุณสามารถติดตามได้ด้วยแดชบอร์ด observability
หากคุณคาดว่าโครงสร้างข้อมูลหรือคีย์รายการจะได้รับอัตราคำขอที่สูงและต่อเนื่อง ให้ shard เพื่อให้โหลดกระจายไปยังพาร์ติชันหรือคีย์มากขึ้น กระจายการอ่านและการเขียนอย่างสม่ำเสมอไปยังหลายคีย์รายการเพื่อให้คงอยู่ภายในขีดจำกัดต่อคีย์ คุณยังสามารถลดอัตราคำขอโดยการแคชค่าในเซิร์ฟเวอร์และตรวจสอบอีกครั้งหลังจากช่วงเวลา, การจัดกลุ่มคำขอเมื่อเป็นไปได้, และใช้ exponential backoff เมื่อคุณได้รับการตอบสนองที่ถูกจำกัด
แนวทางปฏิบัติที่ดีที่สุด
เพื่อให้รูปแบบการใช้งานหน่วยความจำของคุณอยู่ในระดับที่เหมาะสมและหลีกเลี่ยงการถึง limits ให้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้:
ลบรายการที่ประมวลผลแล้ว การทำความสะอาดรายการที่อ่านอย่างสม่ำเสมอโดยใช้วิธี MemoryStoreQueue:RemoveAsync() สำหรับ queues และ MemoryStoreSortedMap:RemoveAsync() สำหรับ sorted maps สามารถปล่อยหน่วยความจำและทำให้โครงสร้างข้อมูลทันสมัย
ตั้งเวลาในการหมดอายุให้สั้นที่สุดเท่าที่จะเป็นไปได้เมื่อเพิ่มข้อมูล แม้ว่าเวลาหมดอายุเริ่มต้นจะเป็น 45 วันสำหรับทั้ง MemoryStoreQueue:AddAsync() และ MemoryStoreSortedMap:SetAsync() การตั้งเวลาให้สั้นที่สุดสามารถทำให้ข้อมูลเก่าถูกลบโดยอัตโนมัติเพื่อป้องกันไม่ให้เติมโควต้าการใช้งานหน่วยความจำของคุณ
- อย่าเก็บข้อมูลจำนวนมากที่มีเวลาหมดอายุยาว เพราะอาจเสี่ยงต่อการเกินโควต้าหน่วยความจำและอาจทำให้เกิดปัญหาที่อาจทำให้เกมของคุณเสียหาย
- ควรลบรายการที่ไม่จำเป็นอย่างชัดเจนหรือกำหนดเวลาหมดอายุให้สั้น
- โดยทั่วไป คุณควรใช้การลบอย่างชัดเจนเพื่อปล่อยหน่วยความจำและการหมดอายุของรายการเป็นกลไกป้องกันเพื่อป้องกันไม่ให้รายการที่ไม่ได้ใช้งานใช้หน่วยความจำเป็นระยะเวลานาน
เก็บค่าในหน่วยความจำเฉพาะที่จำเป็นเท่านั้น
ตัวอย่างเช่น สำหรับเกมบ้านประมูล คุณเพียงต้องรักษาการเสนอราคาสูงสุด คุณสามารถใช้ MemoryStoreSortedMap:UpdateAsync() บนคีย์เดียวเพื่อรักษาการเสนอราคาสูงสุดแทนที่จะเก็บการเสนอราคาทั้งหมดในโครงสร้างข้อมูลของคุณ
ใช้ exponential backoff เพื่อช่วยให้คุณอยู่ต่ำกว่าขีดจำกัดการเรียก API
ตัวอย่างเช่น หากคุณได้รับ DataUpdateConflict คุณอาจลองใหม่หลังจากสองวินาที จากนั้นสี่ แปด เป็นต้น แทนที่จะส่งคำขอไปยัง MemoryStoreService อย่างต่อเนื่องเพื่อให้ได้การตอบสนองที่ถูกต้อง
แบ่งโครงสร้างข้อมูลขนาดใหญ่เป็นหลายโครงสร้างขนาดเล็กโดยการ sharding
มักจะจัดการข้อมูลในโครงสร้างขนาดเล็กได้ง่ายกว่าการเก็บทุกอย่างในโครงสร้างข้อมูลขนาดใหญ่เพียงโครงสร้างเดียว วิธีนี้ยังช่วยหลีกเลี่ยงการใช้งานและขีดจำกัดอัตราได้ ตัวอย่างเช่น หากคุณมี sorted map ที่ใช้คีย์ที่มีพรีฟิก ให้พิจารณาแยกแต่ละพรีฟิกออกเป็น sorted map ของตนเอง สำหรับเกมที่มีความนิยมสูง คุณอาจแยกผู้ใช้เป็นหลายแผนที่ตามหลักฐานสุดท้ายของ ID ผู้ใช้ของพวกเขา
Shard คีย์ที่เข้าถึงบ่อยใน hash maps โดยใช้สำเนาหลายชุดของคีย์เพื่อกระจายโหลด
บีบอัดค่าที่เก็บ
ตัวอย่างเช่น พิจารณาใช้ LZW อัลกอริธึมเพื่อลดขนาดค่าที่เก็บ
ลงทะเบียนในบริการขยาย
คุณสามารถเพิ่มโควต้า Storage และ Request Limit ของคุณโดยการเข้าร่วม Extended Services
การสังเกต
แดชบอร์ด Observability ให้ข้อมูลเชิงลึกและการวิเคราะห์สำหรับการติดตามและแก้ไขปัญหาการใช้งานหน่วยความจำของคุณ ด้วยแผนภูมิที่อัปเดตแบบเรียลไทม์ในด้านต่างๆ ของการใช้งานหน่วยความจำและการเรียก API คุณสามารถติดตามรูปแบบการใช้งานหน่วยความจำของเกมของคุณ ดูโควต้าที่จัดสรรในปัจจุบัน ตรวจสอบสถานะ API และระบุปัญหาที่อาจเกิดขึ้นเพื่อปรับปรุงประสิทธิภาพ
ตารางต่อไปนี้แสดงและอธิบายรหัสสถานะทั้งหมดของการตอบสนอง API ที่มีอยู่ในแผนภูมิ Request Count by Status และ Requests by API x Status ของแดชบอร์ด Observability สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการแก้ไขข้อผิดพลาดเหล่านี้ โปรดดูที่ Troubleshooting สำหรับโควตาหรือขีดจำกัดเฉพาะที่ข้อผิดพลาดเกี่ยวข้อง โปรดดูที่ Limits and Quotas.
| รหัสสถานะ | คำอธิบาย |
|---|---|
| Success | สำเร็จ. |
| DataStructureMemoryOverLimit | เกินขีดจำกัดขนาดหน่วยความจำระดับโครงสร้างข้อมูล (100 MB). |
| DataUpdateConflict | เกิดความขัดแย้งเนื่องจากการอัปเดตพร้อมกัน. |
| AccessDenied | ไม่ได้รับอนุญาตให้เข้าถึงข้อมูลเกม คำขอนี้ไม่ใช้หน่วยคำขอหรือใช้โควต้า. |
| InternalError | ข้อผิดพลาดภายใน. |
| InvalidRequest | คำขอไม่มีข้อมูลที่จำเป็นหรือมีข้อมูลที่ไม่ถูกต้อง. |
| DataStructureItemsOverLimit | เกินขีดจำกัดจำนวนรายการระดับโครงสร้างข้อมูล (1M). |
| NoItemFound | ไม่พบรายการใน MemoryStoreQueue:ReadAsync() หรือ MemoryStoreSortedMap:UpdateAsync() ReadAsync() จะทำการตรวจสอบทุก 2 วินาทีและส่งคืนรหัสสถานะนี้จนกว่าจะพบรายการในคิว. |
| DataStructureRequestsOverLimit | เกินขีดจำกัดหน่วยคำขอระดับโครงสร้างข้อมูล (100,000 request units ต่อหนึ่งนาที). |
| PartitionRequestsOverLimit | เกิน ขีดจำกัดหน่วยคำขอแบบต่อพาร์ติชันหรือแบบต่อคีย์. |
| TotalRequestsOverLimit | เกินขีดจำกัดหน่วยคำขอระดับจักรวาล. |
| TotalMemoryOverLimit | เกินโควต้าหน่วยความจำระดับจักรวาล. |
| ItemValueSizeTooLarge | ขนาดค่ามากเกินไป (32 KB). |
ตารางต่อไปนี้แสดงรหัสสถานะจากฝั่งลูกค้า ซึ่งขณะนี้ไม่สามารถใช้ได้ในแดชบอร์ด Observability
| รหัสสถานะ | คำอธิบาย |
|---|---|
| InternalError | ข้อผิดพลาดภายใน. |
| UnpublishedPlace | คุณต้องเผยแพร่สถานที่นี้เพื่อใช้ MemoryStoreService. |
| InvalidClientAccess | MemoryStoreService ต้องถูกเรียกจากเซิร์ฟเวอร์. |
| InvalidExpirationTime | ฟิลด์ 'expiration' ต้องอยู่ระหว่าง 0 ถึง 3,888,000. |
| InvalidRequest | ไม่สามารถแปลงค่าเป็น json. |
| InvalidRequest | ไม่สามารถแปลง sortKey เป็นหมายเลขหรือสตริงที่ถูกต้อง. |
| TransformCallbackFailed | ไม่สามารถเรียกใช้ฟังก์ชัน callback การแปลงได้. |
| RequestThrottled | คำขอ MemoryStores ล่าสุดเกินหนึ่งหรือหลายขีดจำกัด. |
| UpdateConflict | เกินจำนวนครั้งสูงสุดในการลอง. |
การแก้ไขปัญหา
ตารางต่อไปนี้แสดงและอธิบายวิธีแก้ไขที่แนะนำสำหรับรหัสสถานะการตอบสนองแต่ละรายการ:
| ข้อผิดพลาด | ตัวเลือกการแก้ไขปัญหา |
|---|---|
| DataStructureRequestsOverLimit / PartitionRequestsOverLimit |
|
| TotalRequestsOverLimit | |
| DataStructureItemsOverLimit |
|
| DataStructureMemoryOverLimit | |
| TotalMemoryOverLimit | |
| DataUpdateConflict |
ตัวอย่างการยกเลิกคำขอ |
| Internal Error |
|
| InvalidRequest |
|
| ItemValueSizeTooLarge |
|
ทดสอบและแก้ไขใน Studio
ข้อมูลใน MemoryStoreService จะถูกแยกออกระหว่าง Studio และการผลิต ดังนั้นการเปลี่ยนแปลงข้อมูลใน Studio จะไม่ส่งผลต่อพฤติกรรมการผลิต ซึ่งหมายความว่าการเรียก API ของคุณจาก Studio จะไม่เข้าถึงข้อมูลการผลิต ทำให้คุณสามารถทดสอบหน่วยความจำจัดเก็บและฟีเจอร์ใหม่ได้อย่างปลอดภัยก่อนที่จะนำไปใช้ในผลิต
การทดสอบใน Studio มี limits and quotas เหมือนกับการผลิต สำหรับโควต้าที่คำนวณตามจำนวนผู้ใช้ โควต้าที่ได้อาจมีขนาดเล็กมากเนื่องจากคุณเป็นผู้ใช้เพียงคนเดียวสำหรับการทดสอบใน Studio เมื่อทดสอบจาก Studio คุณอาจสังเกตเห็นความหน่วงที่สูงขึ้นเล็กน้อยและอัตราข้อผิดพลาดที่สูงขึ้นเมื่อเปรียบเทียบกับการใช้งานในผลิตเนื่องจากมีการตรวจสอบเพิ่มเติมที่ดำเนินการเพื่อยืนยันการเข้าถึงและสิทธิ์
สำหรับข้อมูลเกี่ยวกับวิธีการแก้ไขหน่วยความจำจัดเก็บในเกมสดหรือเมื่อทดสอบใน Studio ให้ใช้ Developer Console.