ใช้แนวทางปฏิบัติเหล่านี้ในการจัดระเบียบข้อมูลชั่วคราว กระจายภาระงาน และตอบสนองต่อปัญหาหน่วยความจำ
ขึ้นอยู่กับประเภทของโครงสร้างข้อมูล MemoryStoreService จะบังคับใช้ ข้อจำกัด เกี่ยวกับหน่วยความจำและจำนวนรายการในโครงสร้างข้อมูล โครงสร้างข้อมูลทั้งหมดยังถูกจำกัดโดยข้อจำกัดการร้องขอทั่วไประหว่างพาร์ติชัน
ออกแบบคีย์และโครงสร้างข้อมูล
ดู ใช้รูปแบบคีย์แบบคงที่และคำนำหน้า ใน แนวทางปฏิบัติที่ดีที่สุดสำหรับหน่วยความจำ ใช้รูปแบบเหล่านั้นกับทั้งคีย์และชื่อโครงสร้างข้อมูลเพื่อให้เซิร์ฟเวอร์ทุกเครื่องส่งข้อมูลเชิงตรรกะเดียวกันไปยังตำแหน่งเดียวกัน
เลือกเวลาหมดอายุที่ตรงกับระยะเวลาที่ข้อมูลยังมีประโยชน์ อย่าใช้หน่วยความจำสำหรับบันทึกผู้เล่นถาวรหรือข้อมูลที่ต้องอยู่รอดหลังจากหมดอายุ สำหรับความช่วยเหลือในการเลือกบริการระหว่างกัน ดู หน่วยความจำกับหน่วยความจำ
จัดการข้อผิดพลาดในการร้องขอ
ดู ลองทำซ้ำข้อผิดพลาดชั่วคราว ใน แนวทางปฏิบัติที่ดีที่สุดสำหรับหน่วยความจำ
ชอบ UpdateAsync มากกว่า SetAsync
ดู ชอบ UpdateAsync มากกว่า SetAsync ใน แนวทางปฏิบัติที่ดีที่สุดสำหรับหน่วยความจำ แฮชแมพและแผนที่ที่เรียงลำดับให้บริการ MemoryStoreHashMap:UpdateAsync() และ MemoryStoreSortedMap:UpdateAsync() สำหรับรูปแบบนี้
กระจายการร้องขอที่เกิดขึ้นซ้ำ
ดู กระจายการร้องขอที่เกิดขึ้นซ้ำ ใน แนวทางปฏิบัติที่ดีที่สุดสำหรับหน่วยความจำ
ตรวจสอบการใช้งาน
ใช้ แดชบอร์ดการสังเกตการณ์หน่วยความจำ เพื่อตรวจสอบการใช้งานโควตา ปริมาณการร้องขอ และสถานะการตอบสนอง ตรวจสอบการแจ้งเตือนทางอีเมลที่มีอยู่ ตั้งค่า การแจ้งเตือนที่กำหนดเอง สำหรับเมตริกหน่วยความจำที่สำคัญ และใช้ รายงานข้อผิดพลาด เพื่อตรวจสอบข้อผิดพลาด
ลดการร้องขอ ขนาดรายการ และเวลาหมดอายุก่อนที่จะเพิ่มความจุ หากการใช้งานที่ถูกต้องเกินโควตาเริ่มต้น ให้ประเมิน บริการขยาย
จัดการข้อจำกัดของแผนที่ที่เรียงลำดับและคิว
แผนที่ที่เรียงลำดับและคิวมีข้อจำกัดเกี่ยวกับจำนวนรายการสูงสุดและหน่วยความจำรวมสูงสุด นอกจากนี้ รายการในโครงสร้างข้อมูลเหล่านี้จะอยู่ในพาร์ติชันเดียวกันเสมอ การร้องขอแต่ละครั้งไปยังโครงสร้างข้อมูลเหล่านั้นคือการร้องขอไปยังพาร์ติชันเดียวกัน
เมื่อแผนที่ที่เรียงลำดับหรือคิวถึงขีดจำกัดของรายการหรือหน่วยความจำ ให้ลบรายการที่ไม่จำเป็นด้วยตนเองหรือโดยการเพิ่มนโยบายหมดอายุ หากขีดจำกัดหน่วยความจำเป็นสาเหตุของการจำกัด ให้ลดขนาดรายการโดยการลบข้อมูลที่ไม่จำเป็นออกจากคีย์และค่า
หากคุณต้องการรายการทั้งหมดหรือประสบปัญหาการจำกัดเนื่องจากปริมาณการร้องขอ วิธีเดียวที่จะทำได้คือการแบ่งส่วน
กระจายภาระงานด้วยการแบ่งส่วน
การแบ่งส่วนคือกระบวนการจัดเก็บชุดข้อมูลที่เกี่ยวข้องในหลายโครงสร้างข้อมูล กล่าวอีกนัยหนึ่งหมายถึงการนำโครงสร้างข้อมูลที่มีปริมาณการใช้งานสูงที่มีอยู่แล้วมาแทนที่ด้วยโครงสร้างข้อมูลที่เล็กกว่าหลายชุดซึ่งรวมกันมีชุดข้อมูลเดียวกันกับต้นฉบับ
ความท้าทายหลักในการแบ่งส่วนคือการหาวิธีกระจายข้อมูลไปยังโครงสร้างข้อมูลหลายชุดในลักษณะที่รักษาฟังก์ชันการทำงานเดียวกันกับต้นฉบับ
แม้ว่า Roblox จะมีการแบ่งพาร์ติชันแฮชแมพอยู่แล้ว แต่คุณสามารถแบ่งส่วนเพิ่มเติมได้โดยการกระจายการร้องขอไปยังคีย์หลายคีย์
การแบ่งส่วนแผนที่ที่เรียงลำดับ
ในการแบ่งส่วนบันทึกผู้เล่นในแผนที่ที่เรียงลำดับ ให้ใช้คณิตศาสตร์โมดูลเพื่อกำหนด Id แต่ละรายการไปยังแผนที่ที่มีจำนวนคงที่ ตัวอย่างต่อไปนี้ใช้แผนที่สี่แผนที่และส่งผู้ใช้เดียวกันไปยังแผนที่เดียวกันอย่างสม่ำเสมอ:
-- เริ่มต้นบริการ MemoryStore
local MemoryStoreService = game:GetService("MemoryStoreService")
-- สร้างถังแผนที่ที่เรียงลำดับของคุณ
local sm1 = MemoryStoreService:GetSortedMap("sm1")
local sm2 = MemoryStoreService:GetSortedMap("sm2")
local sm3 = MemoryStoreService:GetSortedMap("sm3")
local sm4 = MemoryStoreService:GetSortedMap("sm4")
local sortedMaps = { sm1, sm2, sm3, sm4 }
-- ฟังก์ชันช่วยในการดึงถังที่ถูกต้องจากคีย์รายการ
local function getSortedMapBucket(userId)
local bucketIndex = (userId % #sortedMaps) + 1
return sortedMaps[bucketIndex]
end
-- เริ่มต้นผู้เล่นด้วยค่าดีฟอลต์ 0
for _, player in game:GetService("Players"):GetPlayers() do
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
bucket:SetAsync(tostring(userId), 0, 600)
end
-- ดึงค่าของผู้เล่น
local player = game:GetService("Players"):GetPlayers()[1]
local userId = player.User.Id
local bucket = getSortedMapBucket(userId)
local playerScore = bucket:GetAsync(tostring(userId))
print(playerScore)การแบ่งส่วนคิว
การแบ่งส่วนคิวซับซ้อนกว่าการแบ่งส่วนแผนที่ที่เรียงลำดับ แม้ว่าคุณต้องการกระจายปริมาณการร้องขอไปยังคิวหลายชุด แต่การเพิ่ม การอ่าน และการลบจะเกิดขึ้นที่ด้านหน้าและด้านหลังของคิวเท่านั้น
วิธีแก้ปัญหาหนึ่งคือการใช้คิวหมุน ซึ่งหมายถึงการสร้างคิวหลายชุดและหมุนระหว่างพวกเขาเมื่อคุณเพิ่มหรืออ่านรายการ:
- สร้างคิวหลายชุดและเพิ่มลงในอาร์เรย์
- สร้างตัวชี้ท้องถิ่นสองตัว ตัวหนึ่งแสดงถึงคิวที่คุณต้องการอ่านและลบรายการออก อีกตัวหนึ่งแสดงถึงคิวที่คุณต้องการเพิ่มรายการเข้าไป:
- สำหรับการดำเนินการอ่าน ให้คำนวณจำนวนรายการที่คุณต้องการจากแต่ละคิว รวมถึงตำแหน่งที่คุณจะย้ายตัวชี้การอ่านไป
- สำหรับการดำเนินการลบ ให้ส่ง ID จากการอ่านไปยังแต่ละคิว
- สำหรับการดำเนินการเพิ่ม ให้เพิ่มไปยังคิวที่ตัวชี้การเพิ่มและเพิ่มค่าตัวชี้
-- เริ่มต้นบริการ 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[i]
queue:RemoveAsync(ids[i])
end
end
-- สร้างฟังก์ชันท้องถิ่นที่เพิ่มรายการลงในคิว
local function addToQueue(itemKey, expiration, priority)
local queue = queueArr[addIndex]
queue:AddAsync(itemKey, expiration, priority)
addIndex = rotateIndex(addIndex, 1)
end
-- เขียนโค้ดบางส่วน!
for _, player in game:GetService("Players"):GetPlayers() do
addToQueue(player.User.Id, 600, 0)
end
local players, ids = readFromQueue(20, true, -1)
removeFromQueue(ids)แฮชแมพ
แฮชแมพไม่มีข้อจำกัดด้านหน่วยความจำหรือจำนวนรายการแต่ละรายการและถูกแบ่งส่วนโดยอัตโนมัติ แต่คุณยังสามารถประสบปัญหาการจำกัดหากคุณใช้มันไม่ถูกต้อง
ตัวอย่างเช่น พิจารณาเกมที่มีแฮชแมพของข้อมูลที่เก็บเป็นค่าของคีย์เดียวที่ชื่อว่า metadata หากข้อมูลเมตานี้มีวัตถุที่ซ้อนกันซึ่งมีข้อมูลเช่น ID สถานที่ จำนวนผู้เล่น และอื่นๆ ทุกครั้งที่ต้องการข้อมูลเมตานี้ คุณไม่มีทางเลือกอื่นนอกจากต้องเรียก GetAsync("metadata") และดึงวัตถุทั้งหมด ในกรณีนี้ การร้องขอทั้งหมดจะไปยังคีย์เดียวและดังนั้นจึงไปยังพาร์ติชันเดียว
แทนที่จะเก็บข้อมูลเมตาทั้งหมดเป็นวัตถุที่ซ้อนกันเดียว ให้เก็บแต่ละฟิลด์ที่เข้าถึงได้อย่างอิสระเป็นคีย์ของตนเองเพื่อให้แฮชแมพสามารถใช้ประโยชน์จากการแบ่งส่วนโดยอัตโนมัติ หากคุณต้องการการแยกระหว่างข้อมูลเมตากับแฮชแมพที่เหลือ ให้เพิ่มคำนำหน้าในการตั้งชื่อ เช่น metadata_user_count แทนที่จะเป็น user_count
หากคีย์หนึ่งหรือไม่กี่คีย์ได้รับการร้องขอบ่อยๆ ให้แบ่งส่วนการเรียกเหล่านั้นไปยังคีย์หลายคีย์ ตัวอย่างเช่น หากเซิร์ฟเวอร์เกมทั้งหมดดึงค่าจากคีย์แฮชแมพเดียว การร้องขออาจทำให้เกิดการจำกัดพาร์ติชัน เพื่อลดภาระงาน ให้คัดลอกค่าไปยังคีย์หลายคีย์และส่งแต่ละเซิร์ฟเวอร์ไปยังชิ้นส่วนที่เสถียร