คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL

การแนะนำ

คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL “คุณต้องวิ่งให้เร็วที่สุดเพื่อที่จะอยู่กับที่
และเพื่อที่จะไปที่ไหนสักแห่ง คุณต้องวิ่งให้เร็วขึ้นอย่างน้อยสองเท่า!”
(ค) อลิซในแดนมหัศจรรย์

คราวที่แล้วผมถูกขอให้บรรยาย นักวิเคราะห์ บริษัท ของเราในหัวข้อการออกแบบแบบจำลองข้อมูลเนื่องจากการนั่งอยู่ในโครงการเป็นเวลานาน (บางครั้งเป็นเวลาหลายปี) เรามองไม่เห็นสิ่งที่เกิดขึ้นรอบตัวเราในโลกของเทคโนโลยีไอที ในบริษัทของเรา (มันเกิดขึ้นอย่างนั้น) หลายโครงการไม่ได้ใช้ฐานข้อมูล NoSQL (อย่างน้อยก็ในตอนนี้) ดังนั้นในการบรรยายของฉัน ฉันจึงให้ความสนใจกับพวกเขาแยกกันโดยใช้ตัวอย่างของ HBase และพยายามปรับทิศทางการนำเสนอเนื้อหาให้กับสิ่งเหล่านั้น ที่ไม่เคยใช้ก็ได้ผล โดยเฉพาะอย่างยิ่ง ฉันแสดงให้เห็นคุณลักษณะบางอย่างของการออกแบบแบบจำลองข้อมูลโดยใช้ตัวอย่างที่ฉันอ่านเมื่อหลายปีก่อน ในบทความ “ความรู้เบื้องต้นเกี่ยวกับการออกแบบ HB ase Schema” โดย Amandeep Khurana. เมื่อวิเคราะห์ตัวอย่าง ฉันเปรียบเทียบหลายตัวเลือกในการแก้ปัญหาเดียวกันเพื่อถ่ายทอดแนวคิดหลักให้กับผู้ชมได้ดียิ่งขึ้น

เมื่อเร็ว ๆ นี้ "ไม่มีอะไรทำ" ฉันถามตัวเองด้วยคำถาม (ช่วงสุดสัปดาห์ที่ยาวนานของการกักกันเดือนพฤษภาคมเอื้อต่อสิ่งนี้เป็นพิเศษ) การคำนวณทางทฤษฎีจะสอดคล้องกับการปฏิบัติมากน้อยเพียงใด ที่จริงแล้วนี่คือที่มาของแนวคิดสำหรับบทความนี้ นักพัฒนาที่ทำงานกับ NoSQL มาหลายวันอาจไม่ได้เรียนรู้อะไรใหม่จาก NoSQL (และอาจข้ามครึ่งบทความทันที) แต่สำหรับ นักวิเคราะห์สำหรับผู้ที่ยังไม่ได้ทำงานอย่างใกล้ชิดกับ NoSQL ฉันคิดว่ามันจะเป็นประโยชน์ในการทำความเข้าใจพื้นฐานเกี่ยวกับคุณลักษณะของการออกแบบแบบจำลองข้อมูลสำหรับ HBase

ตัวอย่างการวิเคราะห์

ในความคิดของฉัน ก่อนที่คุณจะเริ่มใช้ฐานข้อมูล NoSQL คุณต้องคิดให้รอบคอบและชั่งน้ำหนักข้อดีข้อเสีย บ่อยครั้งที่ปัญหาสามารถแก้ไขได้โดยใช้ DBMS เชิงสัมพันธ์แบบดั้งเดิม ดังนั้นจึงเป็นการดีกว่าที่จะไม่ใช้ NoSQL โดยไม่มีเหตุผลที่สำคัญ หากคุณยังคงตัดสินใจใช้ฐานข้อมูล NoSQL คุณควรคำนึงว่าแนวทางการออกแบบที่นี่ค่อนข้างแตกต่างออกไป โดยเฉพาะอย่างยิ่งบางส่วนอาจไม่ปกติสำหรับผู้ที่เคยจัดการกับ DBMS เชิงสัมพันธ์มาก่อนเท่านั้น (ตามข้อสังเกตของฉัน) ดังนั้น ในโลก "เชิงสัมพันธ์" เรามักจะเริ่มต้นด้วยการสร้างแบบจำลองโดเมนของปัญหา จากนั้นจึงทำให้แบบจำลองเป็นปกติ (denormalize) หากจำเป็น ใน NoSQL เรา ควรคำนึงถึงสถานการณ์ที่คาดหวังในการทำงานกับข้อมูลทันที และเริ่มทำให้ข้อมูลไม่ปกติ นอกจากนี้ยังมีข้อแตกต่างอื่นๆ อีกหลายประการซึ่งจะกล่าวถึงด้านล่าง

ลองพิจารณาปัญหา "สังเคราะห์" ต่อไปนี้ซึ่งเราจะดำเนินการต่อไป:

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

  • ตอบคำถามว่าผู้ใช้ A อ่านผู้ใช้ B หรือไม่ (รูปแบบการอ่าน)
  • อนุญาตให้เพิ่ม/ลบการเชื่อมต่อในกรณีของการสมัครสมาชิก/ยกเลิกการสมัครสมาชิกของผู้ใช้ A จากผู้ใช้ B (เทมเพลตการเปลี่ยนแปลงข้อมูล)

แน่นอนว่ามีหลายทางเลือกในการแก้ปัญหา ในฐานข้อมูลเชิงสัมพันธ์ปกติ เรามักจะสร้างตารางความสัมพันธ์ (อาจเป็นแบบพิมพ์ได้ เช่น ถ้าเราจำเป็นต้องจัดเก็บกลุ่มผู้ใช้ เช่น ครอบครัว ที่ทำงาน ฯลฯ ซึ่งรวมถึง “เพื่อน” นี้ด้วย) และเพื่อเพิ่มประสิทธิภาพ ความเร็วในการเข้าถึงจะเพิ่มดัชนี/การแบ่งพาร์ติชัน เป็นไปได้มากว่าตารางสุดท้ายจะมีลักษณะดังนี้:

user_id
เพื่อน_id

vasya
Petya

vasya
Оля

ต่อไปนี้เพื่อความชัดเจนและความเข้าใจที่ดีขึ้น ข้าพเจ้าจะระบุชื่อแทนบัตรประจำตัว

ในกรณีของ HBase เรารู้ว่า:

  • การค้นหาที่มีประสิทธิภาพที่ไม่ส่งผลให้มีการสแกนตารางทั้งหมดเป็นไปได้ โดยคีย์เท่านั้น
    • นั่นเป็นเหตุผลว่าทำไมการเขียนคำสั่ง SQL ที่คนจำนวนมากคุ้นเคยกับฐานข้อมูลดังกล่าวจึงเป็นความคิดที่ไม่ดี ในทางเทคนิคแล้ว แน่นอน คุณสามารถส่งแบบสอบถาม SQL ด้วย Joins และตรรกะอื่นๆ ไปยัง HBase จาก Impala เดียวกันได้ แต่จะมีประสิทธิภาพเพียงใด...

ดังนั้นเราจึงถูกบังคับให้ใช้ ID ผู้ใช้เป็นรหัส และความคิดแรกของฉันในหัวข้อ “จะเก็บ ID ของเพื่อนได้ที่ไหนและอย่างไร” อาจเป็นแนวคิดที่จะจัดเก็บไว้ในคอลัมน์ ตัวเลือกที่ชัดเจนและ "ไร้เดียงสา" ที่สุดนี้จะมีลักษณะเช่นนี้ (ลองเรียกมันว่า ตัวเลือก 1 (ค่าเริ่มต้น)สำหรับการอ้างอิงเพิ่มเติม):

โรว์คีย์
ลำโพง

vasya
1: เพชรยา
2: โอลยา
3: ดาชา

Petya
1: มาช่า
2: วาสยา

ที่นี่ แต่ละบรรทัดสอดคล้องกับผู้ใช้เครือข่ายหนึ่งราย คอลัมน์มีชื่อ: 1, 2, ... - ตามจำนวนเพื่อนและ ID ของเพื่อนจะถูกเก็บไว้ในคอลัมน์ สิ่งสำคัญคือต้องทราบว่าแต่ละแถวจะมีจำนวนคอลัมน์ที่แตกต่างกัน ในตัวอย่างในรูปด้านบน แถวหนึ่งมีสามคอลัมน์ (1, 2 และ 3) และแถวที่สองมีเพียงสอง (1 และ 2) - ที่นี่เราใช้คุณสมบัติ HBase สองตัวที่ฐานข้อมูลเชิงสัมพันธ์ไม่มี:

  • ความสามารถในการเปลี่ยนองค์ประกอบของคอลัมน์แบบไดนามิก (เพิ่มเพื่อน -> เพิ่มคอลัมน์ ลบเพื่อน -> ลบคอลัมน์)
  • แถวที่ต่างกันอาจมีองค์ประกอบของคอลัมน์ต่างกัน

มาตรวจสอบโครงสร้างของเราว่าสอดคล้องกับข้อกำหนดของงาน:

  • การอ่านข้อมูล: เพื่อให้เข้าใจว่า Vasya สมัครสมาชิก Olya หรือไม่เราจะต้องลบออก ทั้งบรรทัด โดยใช้คีย์ RowKey = "Vasya" และเรียงลำดับค่าคอลัมน์จนกว่าเราจะ "พบ" Olya ในนั้น หรือวนซ้ำค่าของคอลัมน์ทั้งหมด "ไม่ตรงตาม" Olya แล้วส่งคืนคำตอบเป็นเท็จ
  • แก้ไขข้อมูล: เพิ่มเพื่อน: สำหรับงานที่คล้ายกันเราก็ต้องลบออกด้วย ทั้งบรรทัด ใช้คีย์ RowKey = "วาสยา" เพื่อคำนวณจำนวนเพื่อนทั้งหมดของเขา เราต้องการจำนวนเพื่อนทั้งหมดเพื่อกำหนดจำนวนคอลัมน์ที่เราต้องจด ID ของเพื่อนใหม่
  • การเปลี่ยนแปลงข้อมูล: การลบเพื่อน:
    • จำเป็นต้องลบ ทั้งบรรทัด ด้วยคีย์ RowKey = "Vasya" และเรียงลำดับตามคอลัมน์เพื่อค้นหาคอลัมน์ที่จะบันทึกเพื่อนที่จะลบ
    • ต่อไปหลังจากลบเพื่อนแล้ว เราต้อง "เปลี่ยน" ข้อมูลทั้งหมดลงในคอลัมน์เดียวเพื่อไม่ให้มี "ช่องว่าง" ในการกำหนดหมายเลข

ตอนนี้เรามาประเมินว่าอัลกอริธึมเหล่านี้ซึ่งเราจะต้องนำไปใช้ในด้าน "แอปพลิเคชันแบบมีเงื่อนไข" มีประสิทธิผลเพียงใดโดยใช้ O-สัญลักษณ์. ลองแสดงขนาดของเครือข่ายโซเชียลสมมุติของเราเป็น n จำนวนเพื่อนสูงสุดที่ผู้ใช้หนึ่งคนสามารถมีได้คือ (n-1) เราอาจละเลยสิ่งนี้ (-1) เพื่อวัตถุประสงค์ของเราได้ เนื่องจากภายในกรอบการใช้สัญลักษณ์ O นั้นไม่สำคัญ

  • การอ่านข้อมูล: จำเป็นต้องลบทั้งบรรทัดและวนซ้ำคอลัมน์ทั้งหมดในขีดจำกัด ซึ่งหมายความว่าประมาณการต้นทุนส่วนบนจะอยู่ที่ประมาณ O(n)
  • แก้ไขข้อมูล: เพิ่มเพื่อน: เพื่อกำหนดจำนวนเพื่อน คุณต้องวนซ้ำคอลัมน์ทั้งหมดของแถว จากนั้นจึงแทรกคอลัมน์ใหม่ => O(n)
  • การเปลี่ยนแปลงข้อมูล: การลบเพื่อน:
    • คล้ายกับการเพิ่ม - คุณต้องผ่านคอลัมน์ทั้งหมดในขีดจำกัด => O(n)
    • หลังจากลบคอลัมน์แล้ว เราจำเป็นต้อง "ย้าย" คอลัมน์เหล่านั้น หากคุณใช้ "head-on" นี้ คุณจะต้องดำเนินการมากถึง (n-1) ในขีดจำกัด แต่ในส่วนการปฏิบัติเราจะใช้แนวทางที่แตกต่างออกไปซึ่งจะใช้ "pseudo-shift" สำหรับการดำเนินการในจำนวนที่แน่นอน - นั่นคือจะใช้เวลาคงที่กับมันโดยไม่คำนึงถึง n เวลาคงที่ (O(2) ที่แน่นอน) สามารถละเลยได้เมื่อเปรียบเทียบกับ O(n) วิธีการนี้แสดงไว้ในรูปด้านล่าง: เราเพียงคัดลอกข้อมูลจากคอลัมน์ "สุดท้าย" ไปยังคอลัมน์ที่เราต้องการลบข้อมูล จากนั้นจึงลบคอลัมน์สุดท้าย:
      คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL

โดยรวมแล้ว ในทุกสถานการณ์ เราได้รับความซับซ้อนในการคำนวณเชิงซีมโทติคของ O(n)
คุณอาจสังเกตแล้วว่าเราต้องอ่านแถวทั้งหมดจากฐานข้อมูลเกือบทุกครั้ง และในสองกรณีจากสามกรณี เพื่อที่จะอ่านคอลัมน์ทั้งหมดและคำนวณจำนวนเพื่อนทั้งหมด ดังนั้นเพื่อเป็นความพยายามในการปรับให้เหมาะสม คุณสามารถเพิ่มคอลัมน์ "นับ" ซึ่งจัดเก็บจำนวนเพื่อนทั้งหมดของผู้ใช้เครือข่ายแต่ละราย ในกรณีนี้ เราไม่สามารถอ่านทั้งแถวเพื่อคำนวณจำนวนเพื่อนทั้งหมดได้ แต่อ่านได้เพียงคอลัมน์ "นับ" เพียงคอลัมน์เดียว สิ่งสำคัญคืออย่าลืมอัปเดต "การนับ" เมื่อจัดการข้อมูล ที่. เราได้รับการปรับปรุง ตัวเลือก 2 (นับ):

โรว์คีย์
ลำโพง

vasya
1: เพชรยา
2: โอลยา
3: ดาชา
นับ: 3

Petya
1: มาช่า
2: วาสยา

นับ: 2

เปรียบเทียบกับตัวเลือกแรก:

  • การอ่านข้อมูล: เพื่อรับคำตอบสำหรับคำถาม “ Vasya อ่าน Olya หรือไม่” ไม่มีอะไรเปลี่ยนแปลง => O(n)
  • แก้ไขข้อมูล: เพิ่มเพื่อน: เราได้ทำให้การแทรกเพื่อนใหม่ง่ายขึ้น เนื่องจากตอนนี้เราไม่จำเป็นต้องอ่านทั้งบรรทัดและวนซ้ำคอลัมน์ แต่จะสามารถรับค่าของคอลัมน์ "นับ" เท่านั้น เป็นต้น กำหนดหมายเลขคอลัมน์ทันทีเพื่อแทรกเพื่อนใหม่ สิ่งนี้นำไปสู่การลดความซับซ้อนในการคำนวณเป็น O(1)
  • การเปลี่ยนแปลงข้อมูล: การลบเพื่อน: เมื่อลบเพื่อน เรายังสามารถใช้คอลัมน์นี้เพื่อลดจำนวนการดำเนินการ I/O เมื่อ "เลื่อน" ข้อมูลไปทางซ้ายหนึ่งเซลล์ แต่ความจำเป็นในการวนซ้ำคอลัมน์ต่างๆ เพื่อค้นหาคอลัมน์ที่ต้องการลบยังคงอยู่ ดังนั้น => ​​O(n)
  • ในทางกลับกัน เมื่ออัปเดตข้อมูล เราจำเป็นต้องอัปเดตคอลัมน์ “นับ” ทุกครั้ง แต่ต้องใช้เวลาคงที่ ซึ่งสามารถละเลยได้ภายในกรอบของสัญลักษณ์ O

โดยทั่วไป ตัวเลือกที่ 2 ดูเหมือนจะเหมาะสมกว่าเล็กน้อย แต่ก็เหมือนกับ "วิวัฒนาการมากกว่าการปฏิวัติ" เพื่อจะทำให้เกิด “การปฏิวัติ” เราจำเป็นต้องมี ตัวเลือก 3 (คอลัมน์).
พลิกทุกอย่าง "กลับหัว": เราจะแต่งตั้ง ID ผู้ใช้ชื่อคอลัมน์! สิ่งที่จะเขียนในคอลัมน์นั้นไม่สำคัญสำหรับเราอีกต่อไป ให้เป็นอันดับ 1 (โดยทั่วไปแล้วสิ่งที่มีประโยชน์สามารถเก็บไว้ที่นั่นได้ เช่น กลุ่ม “ครอบครัว/เพื่อน/ฯลฯ”) วิธีการนี้อาจทำให้ "คนธรรมดา" ที่ไม่ได้เตรียมตัวไว้ซึ่งไม่เคยมีประสบการณ์มาก่อนในการทำงานกับฐานข้อมูล NoSQL มาก่อน แต่วิธีนี้ทำให้คุณสามารถใช้ศักยภาพของ HBase ในงานนี้ได้อย่างมีประสิทธิภาพมากขึ้น:

โรว์คีย์
ลำโพง

vasya
เพชรยา: 1
โอลิยา: 1
ดาชา: 1

Petya
มาช่า: 1
วาสยา: 1

ที่นี่เราได้รับข้อดีหลายประการพร้อมกัน เพื่อให้เข้าใจถึงสิ่งเหล่านั้น เรามาวิเคราะห์โครงสร้างใหม่และประเมินความซับซ้อนในการคำนวณกันดีกว่า:

  • การอ่านข้อมูล: เพื่อตอบคำถามว่า Vasya สมัครรับข้อมูล Olya หรือไม่ การอ่านคอลัมน์ "Olya" เพียงคอลัมน์เดียวก็เพียงพอแล้ว ถ้ามี คำตอบคือ True หากไม่ใช่ – False => O(1)
  • แก้ไขข้อมูล: เพิ่มเพื่อน: การเพิ่มเพื่อน: เพียงเพิ่มคอลัมน์ใหม่ “Friend ID” => O(1)
  • การเปลี่ยนแปลงข้อมูล: การลบเพื่อน: เพียงลบคอลัมน์ Friend ID => O(1)

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

คุณอาจสับสนและก้าวต่อไปอีกเล็กน้อยตามเส้นทางของการเพิ่มประสิทธิภาพและลดการดำเนินการ I/O เมื่อเข้าถึงฐานข้อมูล จะเกิดอะไรขึ้นถ้าเราจัดเก็บข้อมูลความสัมพันธ์ทั้งหมดไว้ในคีย์แถวโดยตรง? นั่นคือสร้างคีย์ผสมเช่น userID.friendID? ในกรณีนี้ เราไม่จำเป็นต้องอ่านคอลัมน์ของบรรทัดเลยด้วยซ้ำ (ตัวเลือก 4(แถว)):

โรว์คีย์
ลำโพง

วาสยา.เพ็ตยา
เพชรยา: 1

วาสยา.โอลิยา
โอลิยา: 1

วาสยาดาชา
ดาชา: 1

Petya.Masha
มาช่า: 1

เพชรยา.วาสยา
วาสยา: 1

แน่นอนว่า การประเมินสถานการณ์การจัดการข้อมูลทั้งหมดในโครงสร้างดังกล่าว เช่นเดียวกับในเวอร์ชันก่อนหน้า จะเป็น O(1) ความแตกต่างกับตัวเลือก 3 จะอยู่ที่ประสิทธิภาพของการดำเนินการ I/O ในฐานข้อมูลเท่านั้น

“โค้งคำนับ” สุดท้าย สังเกตได้ง่ายว่าในตัวเลือกที่ 4 คีย์แถวจะมีความยาวผันแปรได้ ซึ่งอาจส่งผลต่อประสิทธิภาพการทำงาน (ในที่นี้เราจำได้ว่า HBase จัดเก็บข้อมูลเป็นชุดไบต์ และแถวในตารางจะจัดเรียงตามคีย์) นอกจากนี้เรายังมีตัวคั่นที่อาจจำเป็นต้องได้รับการจัดการในบางสถานการณ์ เพื่อกำจัดอิทธิพลนี้ คุณสามารถใช้แฮชจาก userID และ friendID และเนื่องจากแฮชทั้งสองจะมีความยาวคงที่ คุณจึงสามารถต่อเข้าด้วยกันได้โดยไม่ต้องใช้ตัวคั่น จากนั้นข้อมูลในตารางจะมีลักษณะดังนี้ (ตัวเลือก 5 (แฮช)):

โรว์คีย์
ลำโพง

dc084ef00e94aef49be885f9b01f51c01918fa783851db0dc1f72f83d33a5994
เพชรยา: 1

dc084ef00e94aef49be885f9b01f51c0f06b7714b5ba522c3cf51328b66fe28a
โอลิยา: 1

dc084ef00e94aef49be885f9b01f51c00d2c2e5d69df6b238754f650d56c896a
ดาชา: 1

1918fa783851db0dc1f72f83d33a59949ee3309645bd2c0775899fca14f311e1
มาช่า: 1

1918fa783851db0dc1f72f83d33a5994dc084ef00e94aef49be885f9b01f51c0
วาสยา: 1

แน่นอนว่าความซับซ้อนของอัลกอริทึมในการทำงานกับโครงสร้างดังกล่าวในสถานการณ์ที่เรากำลังพิจารณาจะเหมือนกับตัวเลือกที่ 4 นั่นคือ O(1)
โดยรวมแล้ว เราจะสรุปการประมาณความซับซ้อนในการคำนวณทั้งหมดของเราไว้ในตารางเดียว:

การเพิ่มเพื่อน
กำลังตรวจสอบเพื่อนอยู่
กำลังถอดเพื่อน

ตัวเลือก 1 (ค่าเริ่มต้น)
O (n)
O (n)
O (n)

ตัวเลือก 2 (นับ)
O (1)
O (n)
O (n)

ตัวเลือก 3 (คอลัมน์)
O (1)
O (1)
O (1)

ตัวเลือก 4 (แถว)
O (1)
O (1)
O (1)

ตัวเลือก 5 (แฮช)
O (1)
O (1)
O (1)

อย่างที่คุณเห็น ตัวเลือกที่ 3-5 ดูเหมือนจะเป็นตัวเลือกที่เหมาะสมที่สุด และในทางทฤษฎีแล้ว รับรองว่าการดำเนินการตามสถานการณ์การจัดการข้อมูลที่จำเป็นทั้งหมดในเวลาคงที่ ในเงื่อนไขของงานของเรา ไม่มีข้อกำหนดที่ชัดเจนในการรับรายชื่อเพื่อนของผู้ใช้ทั้งหมด แต่ในกิจกรรมโครงการจริง มันจะดีสำหรับเราในฐานะนักวิเคราะห์ที่ดีที่จะ "คาดการณ์" ว่างานดังกล่าวอาจเกิดขึ้นและ “กระจายฟาง” ดังนั้นความเห็นอกเห็นใจของฉันจึงอยู่เคียงข้างตัวเลือกที่ 3 แต่มีแนวโน้มว่าในโครงการจริงคำขอนี้จะได้รับการแก้ไขด้วยวิธีอื่นแล้วดังนั้นหากไม่มีวิสัยทัศน์ทั่วไปของปัญหาทั้งหมดจะเป็นการดีกว่าที่จะไม่ทำ ข้อสรุปสุดท้าย

การเตรียมการทดลอง

ฉันต้องการทดสอบข้อโต้แย้งทางทฤษฎีข้างต้นในทางปฏิบัติ - นี่คือเป้าหมายของแนวคิดที่เกิดขึ้นในช่วงวันหยุดยาว ในการดำเนินการนี้ มีความจำเป็นต้องประเมินความเร็วการทำงานของ "แอปพลิเคชันแบบมีเงื่อนไข" ของเราในสถานการณ์ที่อธิบายไว้ทั้งหมดสำหรับการใช้ฐานข้อมูล รวมถึงการเพิ่มขึ้นในเวลานี้ด้วยการเพิ่มขนาดของเครือข่ายโซเชียล (n) พารามิเตอร์เป้าหมายที่เราสนใจและเราจะวัดในระหว่างการทดสอบคือเวลาที่ใช้โดย "แอปพลิเคชันที่มีเงื่อนไข" เพื่อดำเนินการ "การดำเนินธุรกิจ" หนึ่งรายการ “ธุรกรรมทางธุรกิจ” เราหมายถึงสิ่งต่อไปนี้:

  • การเพิ่มเพื่อนใหม่หนึ่งคน
  • ตรวจสอบว่าผู้ใช้ A เป็นเพื่อนของผู้ใช้ B หรือไม่
  • กำลังลบเพื่อนหนึ่งคน

ดังนั้น เมื่อคำนึงถึงข้อกำหนดที่ระบุไว้ในแถลงการณ์เบื้องต้น สถานการณ์การตรวจสอบจึงมีดังนี้:

  • การบันทึกข้อมูล. สุ่มสร้างเครือข่ายเริ่มต้นขนาด n เพื่อให้เข้าใกล้ "โลกแห่งความจริง" มากขึ้น จำนวนเพื่อนที่ผู้ใช้แต่ละคนมีก็เป็นตัวแปรสุ่มเช่นกัน วัดเวลาที่ “แอปพลิเคชันแบบมีเงื่อนไข” ของเราเขียนข้อมูลที่สร้างขึ้นทั้งหมดไปยัง HBase จากนั้นหารเวลาผลลัพธ์ด้วยจำนวนเพื่อนที่เพิ่มทั้งหมด - นี่คือวิธีที่เราได้รับเวลาเฉลี่ยสำหรับ "การดำเนินธุรกิจ" หนึ่งครั้ง
  • การอ่านข้อมูล. สำหรับผู้ใช้แต่ละคน ให้สร้างรายการ "บุคลิกภาพ" ที่คุณต้องการเพื่อรับคำตอบว่าผู้ใช้สมัครรับข้อมูลหรือไม่ ความยาวของรายการ = ประมาณจำนวนเพื่อนของผู้ใช้ และสำหรับครึ่งหนึ่งของเพื่อนที่เลือก คำตอบควรเป็น "ใช่" และอีกครึ่งหนึ่งคือ "ไม่ใช่" การตรวจสอบจะดำเนินการตามลำดับที่คำตอบ "ใช่" และ "ไม่" สลับกัน (นั่นคือในทุก ๆ วินาทีเราจะต้องผ่านคอลัมน์ทั้งหมดของบรรทัดเพื่อดูตัวเลือกที่ 1 และ 2) จากนั้นนำเวลาคัดกรองทั้งหมดมาหารด้วยจำนวนเพื่อนที่ทดสอบเพื่อให้ได้เวลาคัดกรองเฉลี่ยต่อวิชา
  • การลบข้อมูล. ลบเพื่อนทั้งหมดออกจากผู้ใช้ นอกจากนี้ ลำดับการลบจะเป็นแบบสุ่ม (นั่นคือ เรา "สับเปลี่ยน" รายการดั้งเดิมที่ใช้ในการบันทึกข้อมูล) เวลาตรวจสอบทั้งหมดจะถูกหารด้วยจำนวนเพื่อนที่ถูกลบออกเพื่อให้ได้เวลาเฉลี่ยต่อการตรวจสอบ

จำเป็นต้องเรียกใช้สถานการณ์สำหรับตัวเลือกแบบจำลองข้อมูลแต่ละตัวเลือกจากทั้งหมด 5 แบบ และสำหรับขนาดที่แตกต่างกันของเครือข่ายสังคมเพื่อดูว่าเวลาเปลี่ยนแปลงไปอย่างไรเมื่อเติบโตขึ้น ภายใน n เดียว การเชื่อมต่อในเครือข่ายและรายชื่อผู้ใช้ที่จะตรวจสอบจะต้องเหมือนกันสำหรับทั้ง 5 ตัวเลือก
เพื่อความเข้าใจที่ดีขึ้น ด้านล่างนี้คือตัวอย่างข้อมูลที่สร้างขึ้นสำหรับ n= 5 “ตัวสร้าง” ที่เขียนไว้จะสร้างพจนานุกรม ID สามพจนานุกรมเป็นเอาต์พุต:

  • อันแรกมีไว้สำหรับการแทรก
  • ประการที่สองคือสำหรับการตรวจสอบ
  • ที่สาม - สำหรับการลบ

{0: [1], 1: [4, 5, 3, 2, 1], 2: [1, 2], 3: [2, 4, 1, 5, 3], 4: [2, 1]} # всего 15 друзей

{0: [1, 10800], 1: [5, 10800, 2, 10801, 4, 10802], 2: [1, 10800], 3: [3, 10800, 1, 10801, 5, 10802], 4: [2, 10800]} # всего 18 проверяемых субъектов

{0: [1], 1: [1, 3, 2, 5, 4], 2: [1, 2], 3: [4, 1, 2, 3, 5], 4: [1, 2]} # всего 15 друзей

อย่างที่คุณเห็น ID ทั้งหมดที่มากกว่า 10 ในพจนานุกรมสำหรับการตรวจสอบนั้นเป็นรหัสที่จะให้คำตอบเท็จอย่างแน่นอน การแทรก การตรวจสอบ และการลบ "เพื่อน" จะดำเนินการตามลำดับที่ระบุในพจนานุกรมทุกประการ

การทดลองดำเนินการบนแล็ปท็อปที่ใช้ Windows 10 โดยที่ HBase ทำงานในคอนเทนเนอร์ Docker หนึ่งคอนเทนเนอร์ และ Python ที่มี Jupyter Notebook ทำงานในคอนเทนเนอร์อื่น นักเทียบท่าได้รับการจัดสรร CPU 2 คอร์และ RAM 2 GB ตรรกะทั้งหมด เช่น การจำลอง "แอปพลิเคชันแบบมีเงื่อนไข" และ "การวางท่อ" สำหรับการสร้างข้อมูลการทดสอบและเวลาการวัด ถูกเขียนด้วยภาษา Python ไลบรารี่ถูกใช้เพื่อทำงานกับ HBase แฮปปี้เบสเพื่อคำนวณแฮช (MD5) สำหรับตัวเลือก 5 - hashlib

เมื่อคำนึงถึงพลังการประมวลผลของแล็ปท็อปโดยเฉพาะ การเปิดตัวสำหรับ n = 10, 30, … ได้รับการคัดเลือกแบบทดลอง 170 – เมื่อเวลาการทำงานทั้งหมดของรอบการทดสอบทั้งหมด (สถานการณ์ทั้งหมดสำหรับตัวเลือกทั้งหมดสำหรับ n ทั้งหมด) มีความสมเหตุสมผลไม่มากก็น้อยและเหมาะสมระหว่างงานเลี้ยงน้ำชาครั้งหนึ่ง (โดยเฉลี่ย 15 นาที)

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

ผลการทดลอง

การทดสอบแรกคือเวลาที่ใช้ในการกรอกรายชื่อเพื่อนเปลี่ยนแปลงไปอย่างไร ผลลัพธ์อยู่ในกราฟด้านล่าง
คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL
ตามที่คาดไว้ ตัวเลือก 3-5 จะแสดงเวลา "ธุรกรรมทางธุรกิจ" เกือบคงที่ ซึ่งไม่ขึ้นอยู่กับการเติบโตของขนาดเครือข่ายและความแตกต่างในประสิทธิภาพที่แยกไม่ออก
ตัวเลือก 2 ยังแสดงประสิทธิภาพคงที่ แต่แย่ลงเล็กน้อย เกือบ 2 เท่าเมื่อเทียบกับตัวเลือก 3-5 และสิ่งนี้ก็อดไม่ได้ที่จะชื่นชมยินดี เนื่องจากมีความสัมพันธ์กับทฤษฎี - ในเวอร์ชันนี้ จำนวนการดำเนินการ I/O ไปยัง/จาก HBase นั้นมากกว่า 2 เท่าอย่างแน่นอน ข้อมูลนี้สามารถใช้เป็นหลักฐานทางอ้อมว่าโดยหลักการแล้ว โต๊ะทดสอบของเราให้ความแม่นยำที่ดี
ตัวเลือกที่ 1 ตามที่คาดไว้นั้นช้าที่สุดและแสดงให้เห็นถึงการเพิ่มขึ้นเชิงเส้นของเวลาที่ใช้ในการเพิ่มขนาดของเครือข่าย
ตอนนี้เรามาดูผลการทดสอบครั้งที่สองกันดีกว่า
คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL
ตัวเลือก 3-5 ทำงานตามที่คาดไว้อีกครั้ง - เวลาคงที่ โดยไม่ขึ้นอยู่กับขนาดของเครือข่าย ตัวเลือกที่ 1 และ 2 แสดงให้เห็นถึงเวลาที่เพิ่มขึ้นเชิงเส้นเมื่อขนาดเครือข่ายเพิ่มขึ้นและประสิทธิภาพที่ใกล้เคียงกัน ยิ่งกว่านั้น ตัวเลือกที่ 2 ปรากฏว่าช้าลงเล็กน้อย - เห็นได้ชัดว่าเกิดจากความจำเป็นในการพิสูจน์อักษรและประมวลผลคอลัมน์ "การนับ" เพิ่มเติมซึ่งจะสังเกตเห็นได้ชัดเจนยิ่งขึ้นเมื่อ n เติบโตขึ้น แต่ฉันจะยังคงงดเว้นจากการสรุปใด ๆ เนื่องจากความแม่นยำของการเปรียบเทียบนี้ค่อนข้างต่ำ นอกจากนี้อัตราส่วนเหล่านี้ (ตัวเลือกใด 1 หรือ 2 เร็วกว่า) เปลี่ยนจากการวิ่งหนึ่งไปอีกวิ่ง (ในขณะที่ยังคงรักษาลักษณะของการพึ่งพาและ "การวิ่งคอ")

กราฟสุดท้ายคือผลลัพธ์ของการทดสอบการลบออก

คุณสมบัติของการออกแบบโมเดลข้อมูลสำหรับ NoSQL

อีกครั้งไม่มีความประหลาดใจที่นี่ ตัวเลือก 3-5 ดำเนินการลบตามเวลาคงที่
ยิ่งไปกว่านั้น สิ่งที่น่าสนใจคือ ตัวเลือกที่ 4 และ 5 ซึ่งแตกต่างจากสถานการณ์ก่อนหน้านี้ แสดงประสิทธิภาพที่แย่กว่าตัวเลือก 3 เล็กน้อยอย่างเห็นได้ชัด เห็นได้ชัดว่า การดำเนินการลบแถวมีราคาแพงกว่าการดำเนินการลบคอลัมน์ ซึ่งโดยทั่วไปเป็นไปตามตรรกะ

ตัวเลือกที่ 1 และ 2 ตามที่คาดไว้ แสดงให้เห็นถึงการเพิ่มขึ้นเชิงเส้นของเวลา ในเวลาเดียวกัน ตัวเลือก 2 จะช้ากว่าตัวเลือก 1 อย่างสม่ำเสมอ - เนื่องจากมีการดำเนินการ I/O เพิ่มเติมเพื่อ "รักษา" คอลัมน์การนับ

ข้อสรุปทั่วไปของการทดลอง:

  • ตัวเลือก 3-5 แสดงให้เห็นถึงประสิทธิภาพที่มากขึ้นเมื่อใช้ประโยชน์จาก HBase ยิ่งไปกว่านั้น ประสิทธิภาพจะแตกต่างกันโดยสัมพันธ์กันด้วยค่าคงที่ และไม่ขึ้นอยู่กับขนาดของเครือข่าย
  • ไม่มีการบันทึกความแตกต่างระหว่างตัวเลือก 4 และ 5 แต่ไม่ได้หมายความว่าไม่ควรใช้ตัวเลือก 5 มีแนวโน้มว่าสถานการณ์การทดลองที่ใช้โดยคำนึงถึงคุณลักษณะด้านประสิทธิภาพของม้านั่งทดสอบ ไม่อนุญาตให้ตรวจพบได้
  • ลักษณะของการเพิ่มเวลาที่ต้องใช้ในการดำเนิน "การดำเนินธุรกิจ" ด้วยข้อมูลโดยทั่วไปยืนยันการคำนวณทางทฤษฎีที่ได้รับก่อนหน้านี้สำหรับตัวเลือกทั้งหมด

ถ้อยคำส

การทดลองคร่าวๆ ที่ดำเนินการไม่ควรถือเป็นความจริงโดยสมบูรณ์ มีปัจจัยหลายประการที่ไม่ได้นำมาพิจารณาและบิดเบือนผลลัพธ์ (ความผันผวนเหล่านี้จะมองเห็นได้ชัดเจนในกราฟที่มีขนาดเครือข่ายขนาดเล็ก) ตัวอย่างเช่น ความเร็วของความประหยัด ซึ่ง happybase ใช้ ปริมาณและวิธีการนำตรรกะที่ฉันเขียนใน Python ไปใช้ (ฉันไม่สามารถอ้างได้ว่าโค้ดนั้นเขียนอย่างเหมาะสมและมีประสิทธิภาพโดยใช้ความสามารถของส่วนประกอบทั้งหมด) บางที คุณลักษณะของการแคช HBase กิจกรรมเบื้องหลังของ Windows 10 บนแล็ปท็อปของฉัน ฯลฯ โดยทั่วไป เราสามารถสรุปได้ว่าการคำนวณทางทฤษฎีทั้งหมดได้พิสูจน์ความถูกต้องโดยการทดลองแล้ว หรืออย่างน้อยก็ไม่สามารถหักล้างพวกเขาด้วย "การโจมตีแบบตัวต่อตัว" ได้

โดยสรุป คำแนะนำสำหรับทุกคนที่เพิ่งเริ่มออกแบบแบบจำลองข้อมูลใน HBase: นามธรรมจากประสบการณ์ก่อนหน้าในการทำงานกับฐานข้อมูลเชิงสัมพันธ์และจดจำ "คำสั่ง":

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

ที่มา: will.com

เพิ่มความคิดเห็น