การปฏิบัติที่ดีที่สุดเมื่อออกแบบโครงสร้างข้อมูล MemoryStore

*เนื้อหานี้แปลโดยใช้ AI (เวอร์ชัน Beta) และอาจมีข้อผิดพลาด หากต้องการดูหน้านี้เป็นภาษาอังกฤษ ให้คลิกที่นี่

ขึ้นอยู่กับประเภทโครงสร้างข้อมูล MemoryStoreService บังคับ ขีดจํากัด บนหน่วยความจําและจํานวนรายการในโครงสร้างข้อมูลโครงสร้างข้อมูลทั้งหมดยังถูกจำกัดโดยขีดจํากัดคําขอต่อส่วนโดยทั่วไป

แต่ละประสบการณ์ Roblox มีแดชบอร์ดการสังเกตความทรงจำ Memory Store Observability Dashboard ซึ่งรวมถึงชุดของแผนภูมิที่คุณสามารถใช้เพื่อตรวจสอบการใช้งานคลังเก็บความทรงจำ

แผนที่และคิวเรียงลําดับ

แผนที่และคิวที่เรียงลําดับทั้งสองมีข้อจํากัดในจํานวนไอเทมสูงสุดและความทรงจําสูงสุดนอกจากนี้รายการในหนึ่งในโครงสร้างข้อมูลเหล่านี้มักจะอยู่บนพาร์ติชันเดียวเสมอทุกคำขอไปยังหนึ่งในโครงสร้างข้อมูลเหล่านั้นเป็นคำขอไปยังพาร์ติชันเดียวกัน

เมื่อแผนที่หรือคิวที่เรียงลําดับถึงขีดจํากัดรายการหรือหน่วยความจํา การกระทําที่ดีที่สุดคือการลบรายการที่ไม่จําเป็นด้วยตนเองหรือโดยการเพิ่มนโยบายการหมดอายุสําหรับรายการหรือถ้าเฉพาะขีดจํากัดความจําเท่านั้นที่ทําให้เกิดการหดตัวคุณสามารถลองลดขนาดของไอเทมของคุณโดยการเอาข้อมูลที่ไม่จําเป็นออกจากกุญแจและค่าของคุณ

หากคุณต้องการทั้งหมดของไอเทมหรือประสบปัญหาการหดตัวเนื่องจากการร้องขอผ่านทางประสิทธิภาพ การแยกส่วนเป็นทางออกเดียวเท่านั้น

การแยกส่วน

การแยกส่วนคือกระบวนการในการจัดเก็บชุดข้อมูลที่เกี่ยวข้องในหลายโครงสร้างข้อมูลคือการใช้โครงสร้างข้อมูลที่มีอยู่แล้วและมีประสิทธิภาพสูงและแทนที่ด้วยโครงสร้างข้อมูลที่มีขนาดเล็กกว่าหลายรายการที่รวมถึงชุดข้อมูลเดียวกันกับต้นฉบับ

ความท้าทายหลักของการแยกส่วนคือการหาวิธีการแพร่กระจายข้อมูลไปทั่วหลายโครงสร้างข้อมูลในลักษณะที่รักษาฟังก์ชันเดียวกับต้นฉบับ

แยกแผนที่ที่จัดเรียงแล้ว

เพื่อแยกแผนที่ที่จัดเรียงแล้ว พิจารณาแยกข้อมูลของคุณเป็นส่วนย่อยตามตัวอักษรพร้อมช่วงตัวอักษรตัวอย่างเช่น คุณเพียงมีกุญแจที่มีตัวอักษรแรกจาก 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)

แยกคิว

การแยกคิวมีความซับซ้อนมากกว่าการแยกแผนที่ที่เรียงลําดับแม้ว่าคุณต้องการส่งผ่านการร้องขอผ่านทางหลายคิว แต่จะเพิ่ม อ่าน และลบเฉพาะเกิดขึ้นที่ด้านหน้าหรือด้านหลังของคิวเท่านั้น

ทางออกหนึ่งคือการใช้คิวหมุนเวียนซึ่งหมายถึงการสร้างคิวหลายรายการและหมุนระหว่างกันเมื่อคุณเพิ่มหรืออ่านรายการ:

  1. สร้างคิวหลายรายการและเพิ่มลงใน阵列

  2. สร้างสองตัวชี้ท้องถิ่น หนึ่งแทนที่คิวที่คุณต้องการอ่านและลบรายการ อีกแทนที่คิวที่คุณต้องการเพิ่มรายการ:

    • สำหรับการดำเนินการอ่าน คำนวณจํานวนรายการที่คุณต้องการจากแต่ละคิวเช่นเดียวกับสถานที่ที่จะย้ายตัวชี้อ่านไป
    • สำหรับการลบการดำเนินการ ส่งรหัสจากการอ่านไปยังคิวแต่ละคิว
    • สำหรับการเพิ่มการดำเนินการให้เพิ่มลงในคิวที่จุดเพิ่มและเพิ่มจุดชี้ขึ้น
แยกคิว

-- เริ่มบริการ 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 )