วิธีออกแบบฐานข้อมูลสำหรับจัดเก็บรายการที่เรียงลำดับ?


42

ฉันต้องการจัดเก็บรายการที่เรียงลำดับไว้ในฐานข้อมูล ฉันต้องการดำเนินการต่อไปนี้อย่างมีประสิทธิภาพ

  1. Insert (x) - แทรก record x ลงในตาราง
  2. ลบ (x) - ลบบันทึก x จากตาราง
  3. ก่อนหน้า (x, n) - ส่งคืนระเบียน 'n' ก่อนหน้าระเบียน x ในรายการที่เรียงลำดับ
  4. หลังจาก (x, n) - ส่งคืนระเบียน 'n' ที่ประสบความสำเร็จในการบันทึก x ในรายการที่เรียงลำดับ
  5. First (n) - ส่งคืนระเบียน 'n' แรกจากรายการที่เรียงลำดับ
  6. Last (n) - ส่งคืนระเบียน 'n' ล่าสุดจากรายการที่เรียงลำดับ
  7. เปรียบเทียบ (x, y) - ให้สองเรคคอร์ด x และ y จากตารางค้นหาถ้า x> y

วิธีง่าย ๆ ที่ฉันคิดได้ก็คือเก็บแอตทริบิวต์ 'อันดับ' ไว้ในตารางและแบบสอบถามโดยการเรียงลำดับในแอตทริบิวต์นั้น แต่ในวิธีนี้การแทรก / แก้ไขเรคคอร์ดที่มีอันดับจะเป็นการดำเนินการที่มีค่าใช้จ่ายสูง มีวิธีที่ดีกว่านี้ไหม?

โดยเฉพาะฉันต้องการใช้ตารางโดยใช้ SimpleDB ของ Amazon แต่คำตอบทั่วไปสำหรับฐานข้อมูลเชิงสัมพันธ์ก็ควรมีประโยชน์เช่นกัน

อัปเดตเมื่อโหลดโปรไฟล์:

เนื่องจากฉันกำลังวางแผนสิ่งนี้สำหรับเว็บแอปพลิเคชันมันขึ้นอยู่กับจำนวนผู้ใช้ที่ใช้แอป

หากมีผู้ใช้ที่ใช้งาน 100k (การมองในแง่ดีสุด: P) การประมาณการโดยประมาณต่อวันของฉันจะเป็นเช่นนั้น

เลือก 500k, แทรกและลบ 100k, อัปเดต 500k

ฉันคาดว่าตารางจะโตขึ้นมากถึง 500k

ฉันกำลังมองหาที่จะปรับให้เหมาะสมกับการปรับปรุงแทรกและการดำเนินการเปรียบเทียบ อันดับของรายการจะมีการเปลี่ยนแปลงอยู่ตลอดเวลาและฉันจำเป็นต้องปรับปรุงตาราง


อธิบายรายละเอียดโหลดที่คาดไว้เล็กน้อย เลือก / แทรก / อัพเดตได้กี่ครั้งต่อวัน การดำเนินการใดที่คุณต้องการเพิ่มประสิทธิภาพมากที่สุด คุณคาดหวังว่าโต๊ะจะโตต่อวันหรือมีขนาดใหญ่แค่ไหน?
Nick Chammas

นี่คือบอร์ดการจัดอันดับผู้เล่นหรือไม่? อย่างไรก็ตามฉันได้อัปเดตคำตอบของฉันด้านล่างพร้อมข้อเสนอแนะตามโปรไฟล์โหลดที่คาดการณ์
Nick Chammas

ไม่มันไม่ใช่กระดานอันดับนักเตะ
chitti

คุณใช้วิธีการแบบไหน
Nick Chammas

ฉันยังไม่แน่ใจในสิ่งที่ถูกถามที่นี่หรือสิ่งที่คุณไม่จำเป็นต้องทำจากรายการซักผ้าของสิ่งที่คุณต้องทำ
Evan Carroll

คำตอบ:


22

หากลำดับโดยพลการไม่สมบูรณ์ แต่แทนที่จะได้มาจากคุณสมบัติอื่น ๆ (ชื่อเช่นคะแนนผู้เล่นอื่น ๆ ) แล้วจะดูดีที่คำตอบของโจเอล

หากเป็นคุณสมบัติของข้อมูลของคุณเองข้อมูลนั้นควรถูกจัดเก็บเป็นคอลัมน์ในสารบัญของคุณ สมมติว่า SimpleDB ของ Amazon นั้นคล้ายคลึงกับ RDBMS ทั่วไปคุณสามารถสร้างดัชนีคอลัมน์นี้และตอบสนองการสืบค้นทั้งหมดข้างต้นได้อย่างรวดเร็วด้วยกลยุทธ์การจัดทำดัชนีที่เหมาะสม นี่เป็นเรื่องปกติสำหรับ RDBMS

เนื่องจากคุณคาดว่าจะมีการแทรกและอัปเดตกิจกรรมสูง แต่ก็เป็นกิจกรรมการอ่านที่ค่อนข้างสูงฉันขอแนะนำให้ทำสิ่งต่อไปนี้:

  • จัดกลุ่มตารางตามลำดับโดยเฉพาะอย่างยิ่งหากข้อความค้นหาส่วนใหญ่ของคุณไม่ตรงกับอันดับ หากไม่มีหรือหากเลือกคีย์การทำคลัสเตอร์ไม่พร้อมใช้งานใน SimpleDB ให้สร้างดัชนีที่มีอันดับเป็นคอลัมน์นำ สิ่งนี้จะตอบสนองการสืบค้น 3-6
  • ดัชนีในระเบียนก่อนแล้วจึงจัดอันดับ (หรือในโลกของ SQL Server เพียงแค่บันทึกและINCLUDEจัดอันดับ -ing หรือเพียงบันทึกถ้าคุณทำคลัสเตอร์บนอันดับ) จะเป็นไปตามแบบสอบถาม 7
  • การดำเนินการ 1 และ 2 สามารถปรับให้เหมาะสมโดยการกระจายข้อมูลของคุณอย่างเหมาะสม (เช่นการตั้งค่าFILLFACTORใน SQL Server) สิ่งนี้สำคัญอย่างยิ่งหากคุณจัดกลุ่มเป็นอันดับ
  • ในขณะที่คุณแทรกหรืออัปเดตอันดับรักษาช่วงห่างระหว่างหมายเลขอันดับให้มากที่สุดเพื่อลดความเป็นไปได้ที่คุณจะต้องจัดอันดับระเบียนที่มีอยู่อีกครั้งเพื่อรองรับการแทรกอันดับหรือปรับปรุงอันดับ ตัวอย่างเช่นหากคุณจัดอันดับระเบียนของคุณในขั้นตอน 1,000 คุณออกจากพื้นที่เพียงพอสำหรับประมาณครึ่งหนึ่งที่มีการเปลี่ยนแปลงและแทรกจำนวนมากโดยมีโอกาสน้อยที่สุดคุณจะต้องจัดอันดับบันทึกใหม่ที่ไม่เกี่ยวข้องโดยตรงกับการเปลี่ยนแปลงเหล่านั้น
  • ทุกคืนจะจัดอันดับระเบียนทั้งหมดใหม่เพื่อรีเซ็ตช่องว่างอันดับระหว่างพวกเขา
  • คุณสามารถปรับความถี่ของการจัดอันดับซ้ำรวมทั้งขนาดช่องว่างอันดับเพื่อรองรับจำนวนการแทรกหรือการปรับปรุงที่คุณคาดว่าสัมพันธ์กับจำนวนของระเบียนที่มีอยู่ ดังนั้นหากคุณมีบันทึก 100K และคาดว่าส่วนแทรกและการอัปเดตของคุณจะเป็น 10% ของจำนวนนั้นให้เหลือที่ว่างสำหรับอันดับใหม่ 10K และจัดอันดับใหม่ทุกคืน
  • การจัดอันดับระเบียน 500K อีกครั้งเป็นการดำเนินการที่มีราคาแพง แต่การทำวันละครั้งหรือสัปดาห์ละครั้งนอกเวลาควรจะดีสำหรับฐานข้อมูลเช่นนั้น การจัดอันดับใหม่นอกเวลาทำการเพื่อรักษาช่องว่างอันดับคือสิ่งที่ช่วยให้คุณไม่ต้องจัดอันดับระเบียนจำนวนมากอีกครั้งสำหรับการอัปเดตหรือการจัดอันดับแต่ละครั้งในช่วงเวลาปกติและสูงสุด

หากคุณคาดหวัง 100K + อ่านบนตารางขนาด 100K ฉันไม่แนะนำให้ใช้วิธีการเชื่อมโยงรายการ มันจะไม่ขยายขนาดได้ดีกับขนาดเหล่านั้น


อันดับสามารถแก้ไขได้ ฉันคาดหวังว่าอันดับจะมีการเปลี่ยนแปลงอยู่ตลอดเวลาและมีการเพิ่มระเบียนใหม่ ๆ อย่างต่อเนื่อง ฉันกังวลเกี่ยวกับกรณีเมื่อฉันแทรกองค์ประกอบใหม่ที่มีอันดับแล้วอันดับของระเบียนทั้งหมดด้านล่างระเบียนใหม่ในลำดับการจัดเรียงต้องเปลี่ยน นั่นเป็นการดำเนินการที่มีราคาแพงเมื่อฉันมีหลายพันระเบียนในฐานข้อมูลของฉันหรือไม่
chitti

@chitti - อ้านั่นเป็นปัญหา คุณสามารถเว้นระยะห่างจากอันดับของคุณ (เช่น 0, 1,000, 2000, 3000, ... ) และจัดอันดับระเบียนทั้งหมดเป็นระยะ ๆ ตามลำดับช่องว่างที่เติม สิ่งนี้จะไม่ขยายหากคุณคาดว่าจะมีมากกว่าสองหมื่นหลายพันรายการ
Nick Chammas

1
@chitti - นี่มันตลกดีจริง ๆ นี่เป็นสิ่งที่เอ็นจินฐานข้อมูลปัญหาจัดการเมื่อทำการจัดทำดัชนีข้อมูลเพราะพวกเขากำลังสั่งซื้อและสั่งซื้ออีกครั้งเมื่อมีการเพิ่มหรือเปลี่ยนแปลงข้อมูล หากคุณค้นหาFILLFACTORคุณจะเห็นว่าเป็นความตั้งใจที่จะสร้างพื้นที่พิเศษสำหรับบันทึกในดัชนีเช่นเดียวกับช่องว่างอันดับที่ฉันอธิบายสร้างพื้นที่สำหรับการเปลี่ยนแปลงและการแทรกอันดับ
Nick Chammas

2
ขอบคุณสำหรับคำตอบที่อัพเดต 'อันดับ' คือคุณสมบัติของข้อมูลของฉัน ฉันเกือบจะเชื่อว่าคอลัมน์ดัชนีที่กำหนดเองคือสิ่งที่ฉันต้องการ ลองดูลิงค์ SOนี้ด้วยคำถามที่คล้ายกัน คำตอบยอดนิยมให้คำแนะนำเกี่ยวกับวิธีการจัดการคอลัมน์อันดับดังกล่าว
chitti

@chitti - คำตอบที่ยอมรับสำหรับคำถามนั้นดีมาก มันแนะนำวิธีการเดียวกันกับที่ฉันได้อธิบายไว้ที่นี่พร้อมกับข้อเสนอแนะเพิ่มเติมเกี่ยวกับการใช้ทศนิยมแทนจำนวนเต็มเพื่อเพิ่มความยืดหยุ่นในการกำหนดและเปลี่ยนอันดับอย่างมาก หาที่ดี
Nick Chammas

13

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

ทางเลือกอื่นคือการสร้างแบบจำลองข้อมูลเป็นรายการที่เชื่อมโยงโดยใช้คอลัมน์ "คีย์ก่อนหน้า" แบบสะท้อนกลับต่างประเทศบนตาราง:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

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

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


2
รูปแบบรายการที่เชื่อมโยงนั้นเรียบร้อย เพื่อดึงลำดับชั้นดังกล่าวในการสั่งซื้อใน SQL Server คุณจะใช้CTE recursive
Nick Chammas

การสร้างลำดับชั้นนั้นค่อนข้างแพงสำหรับโต๊ะที่สูง ข้อดีคือสามารถเปลี่ยนอันดับ / แทรก / ฯลฯ ได้อย่างง่ายดาย ขึ้นอยู่กับรายละเอียดการโหลดที่คาดว่าจะได้ของ chitti นี่อาจเป็นวิธีที่ดีที่สุด
Nick Chammas

ตัวเลือกรายการที่เชื่อมโยงดูเหมือนความคิดที่ดีที่สุดสำหรับการดำเนินการทั้งหมดยกเว้นการเปรียบเทียบ ความคิดใดที่ฉันจะใช้การเปรียบเทียบโดยไม่ต้องติดตามเส้นทางระหว่างองค์ประกอบทั้งสองที่ถูกเปรียบเทียบ
chitti

หากคุณมี ID ของรายการที่ฉันคิดว่า Compare () จะตรงไปตรงมาเว้นแต่ว่าฉันเข้าใจผิดว่าคุณหมายถึงการเปรียบเทียบ () เมื่อคุณพูดว่า: "find if x> y" คุณหมายถึง "find if x precedes y" หรือไม่ ฉันไม่เห็นว่าเป็นเรื่องง่ายหากไม่มีดัชนีที่กำหนดเองหรือขั้นตอนการจัดเก็บที่จะนำรายการ (หรือคุณลักษณะ CTE ที่น่าสนใจที่กล่าวถึงโดย @Nick)
bpanulla

5
โซลูชันประเภทนี้มีความใกล้เคียงกับโมเดลข้อมูลกราฟ ( en.wikipedia.org/wiki/Graph_theory ) ระบบจัดเก็บข้อมูลที่ปรับให้เหมาะสมสำหรับการจัดเก็บโหนดและขอบกราฟอาจเป็นทางออกที่ดีกว่า RDBMS ฐานข้อมูลแบบ Triple- และ Quad-stores และกราฟอย่าง Neo4J นั้นค่อนข้างดี
bpanulla

6

ฉันคิดว่าสิ่งที่ต้องทำคือการจัดเก็บคุณสมบัติหรือคุณสมบัติที่ใช้ในการคำนวณอันดับแล้วสร้างดัชนีเหนือพวกเขา แทนที่จะพยายามบังคับให้ฐานข้อมูลจัดเก็บข้อมูลตามลำดับหรือใช้รายการลิงก์ที่จัดการด้วยตนเองทำไมไม่ให้เอ็นจิ้นฐานข้อมูลทำในสิ่งที่ถูกออกแบบมาให้ทำ?


2
เกิดอะไรขึ้นถ้า 'คุณสมบัติที่ใช้ในการคำนวณอันดับ' เป็นสิ่งที่ต้องการ? เช่น: รายการชุดตะกร้าสินค้าที่ได้รับการจัดลำดับใหม่ตามการกระทำโดยพลการของผู้ใช้
chitti

เมื่อคุณพูดว่าอันดับเป็นสิ่งที่คุณหมายถึงอะไร จะต้องมีอัลกอริทึมที่คุณใช้ในการคำนวณระดับที่ควรจะเป็น ตัวอย่างเช่น: "อิงจากรายการรถเข็น" - อิงได้อย่างไร จะต้องมีสิ่งที่เก็บไว้ในฐานข้อมูลที่เป็นไดรเวอร์สำหรับการคำนวณอันดับ อาจเป็นการรวมกันของสิ่งต่าง ๆ แต่สิ่งเหล่านี้ต้องถูกเก็บไว้ในตารางลูกค้าหรือในตารางที่เกี่ยวข้องกับลูกค้า หากอยู่ในข้อมูลคุณสามารถสร้างฟังก์ชันที่คำนวณได้ หากคุณสามารถคำนวณได้คุณสามารถจัดเก็บและทำดัชนีได้
Joel Brown

สมมติว่าเราจำเป็นต้องรักษาลำดับของรายการในตะกร้าสินค้าและการสั่งซื้อสามารถเปลี่ยน 'โดยพลการ' โดยผู้ใช้โดยใช้เว็บ UI คุณจะเก็บรายชื่อของรายการในฐานข้อมูลอย่างไรและคุณจะรักษาลำดับการเรียงลำดับอย่างไร
chitti

ถ้าฉันเข้าใจคุณอย่างถูกต้องโดย "การเปลี่ยนแปลงโดยพลการ" ตามลำดับของรายการในตะกร้าสินค้าคุณหมายความว่าผู้ใช้สามารถลากรายการขึ้นและลงในรายการและวางรายการที่พวกเขาต้องการ ฉันเดาว่าจะทำให้ฉันเป็นคนหลอกลวง ทำไมผู้ใช้จะทำเช่นนั้น? หากพวกเขาสามารถทำได้พวกเขาจะทำมันมากหรือไม่? การใช้ลำดับของรายการง่ายๆภายในรถเข็นนั้นเป็นเรื่องเกี่ยวกับประสิทธิภาพหรือไม่? สำหรับฉันแล้วดูเหมือนว่าหมายเลขลำดับจากหนึ่งถึงจำนวนของรายการในรถเข็น + FK ตามลำดับจะให้ดัชนีที่คุณต้องการ เพียงอัปเดตรายการเมื่อมีคนลากไปมา
Joel Brown

3
ตะกร้าสินค้าเป็นเพียงตัวอย่างที่ฉันให้เพื่อแสดงให้เห็นว่ามีหลายกรณีที่ 'อันดับ' สามารถสุ่มได้ อาจเป็นได้ว่าไม่ใช่ตัวอย่างที่ดี คิวดีวีดี netflix อาจเป็นตัวอย่างที่ดีกว่า เพียงเพื่อจินตนาการขอให้จินตนาการถึงคิว netflix ที่มีรายการ 100k ที่ผู้ใช้สามารถจัดลำดับใหม่โดยพลการและเขาทำทุกนาที คุณจะออกแบบฐานข้อมูลเพื่อจัดเก็บรายชื่อภาพยนตร์ที่สั่งในแอปพลิเคชั่นสมมุตินี้ได้อย่างไร?
chitti

1

นี่เป็นข้อ จำกัด ของ non-RDBMS เช่น simpleDB คุณสมบัติที่คุณต้องการไม่สามารถใช้งานได้ในฝั่ง DB ใน SimpleDB แต่จะต้องมีการใช้งานจากด้านการเขียนโปรแกรม / แอปพลิเคชัน

สำหรับ RDBMS ที่ต้องการSQL serverคุณสมบัติที่คุณต้องการจะเป็นพื้นฐานสำหรับดัชนีคลัสเตอร์

  • แทรก (x) - แทรกบันทึก x ลงในตาราง> แทรกอย่างง่าย
  • ลบ (x) - ลบบันทึก x จากตาราง> ลบอย่างง่าย
  • ก่อนหน้า (x, n) - ส่งคืนระเบียน 'n' ก่อนหน้าระเบียน x ในรายการที่เรียงลำดับ > เลือกผลลัพธ์ยอดนิยม n โดยที่ x น้อยกว่ามูลค่าและเรียงลำดับตามข้อ

  • หลังจาก (x, n) - ส่งคืนระเบียน 'n' ที่ประสบความสำเร็จในการบันทึก x ในรายการที่เรียงลำดับ > เลือกผลลัพธ์ยอดนิยม n โดยที่ x มากกว่ามูลค่าและคำสั่งซื้อตามข้อ

  • First (n) - ส่งคืนระเบียน 'n' แรกจากรายการที่เรียงลำดับ > เลือกผลลัพธ์ยอดนิยม n

  • Last (n) - ส่งคืนระเบียน 'n' ล่าสุดจากรายการที่เรียงลำดับ > เลือกผลลัพธ์ยอดนิยม n หลังจากสั่งซื้อโดยเรียง

  • เปรียบเทียบ (x, y) - ให้สองเรคคอร์ด x และ y จากตารางค้นหาถ้า x> y > คำสั่ง TSQL IF

SimpleDB ไม่ให้ดัชนีอัตโนมัติเรียงลำดับและภาษาแบบสอบถามพื้นฐาน ปัญหาของฉันจะยังคงอยู่แม้ว่าฉันเลือก RDBMS ปัญหาเกิดขึ้นเนื่องจากการจัดอันดับของข้อมูลในฐานข้อมูลของฉันเปลี่ยนแปลงโดยพลการและไม่สามารถบันทึกเป็นคุณสมบัติเดียว (เว้นแต่ฉันจะใช้คอลัมน์อันดับที่กำหนดเอง) ที่สามารถทำดัชนีได้
chitti

0

นี่คือสิ่งที่ฉันใช้ในการจัดอันดับตาราง Postgres ของฉันใหม่หลังจากแทรกทุกครั้ง:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.