เหตุใดคุณจึงต้องมีเครื่องมือสนับสนุนสำหรับการแบ่งหน้าบนคีย์

สวัสดีทุกคน! ฉันเป็นนักพัฒนาแบ็กเอนด์ที่เขียนไมโครเซอร์วิสใน Java + Spring ฉันทำงานในทีมพัฒนาผลิตภัณฑ์ภายในทีมหนึ่งของ Tinkoff

เหตุใดคุณจึงต้องมีเครื่องมือสนับสนุนสำหรับการแบ่งหน้าบนคีย์

ในทีมของเรา คำถามเกี่ยวกับการเพิ่มประสิทธิภาพการสืบค้นใน DBMS มักเกิดขึ้น คุณมักจะต้องการที่จะเร็วขึ้นอีกเล็กน้อย แต่คุณไม่สามารถทำได้เสมอไปด้วยดัชนีที่สร้างขึ้นอย่างพิถีพิถัน—คุณต้องมองหาวิธีแก้ปัญหาบางอย่าง ระหว่างการท่องเว็บเพื่อค้นหาการเพิ่มประสิทธิภาพที่เหมาะสมเมื่อทำงานกับฐานข้อมูล ฉันพบว่า บล็อกที่มีประโยชน์ไม่รู้จบของ Marcus Wynandผู้เขียน SQL Performance Explained นี่เป็นบล็อกประเภทหายากที่คุณสามารถอ่านบทความทั้งหมดติดต่อกันได้

ฉันต้องการแปลบทความสั้น ๆ โดย Marcus ให้คุณ สามารถเรียกได้ว่าเป็นแถลงการณ์ที่พยายามดึงดูดความสนใจไปยังปัญหาเก่า แต่ยังคงเกี่ยวข้องกับปัญหาประสิทธิภาพของการดำเนินการออฟเซ็ตตามมาตรฐาน SQL

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

บทนำเล็กน้อย

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

ดังนั้นคีย์เวิร์ด offset จะบอกฐานข้อมูลให้ข้าม n เรคคอร์ดแรกในคำขอ อย่างไรก็ตาม ฐานข้อมูลยังคงจำเป็นต้องอ่าน n บันทึกแรกเหล่านี้จากดิสก์ ตามลำดับที่กำหนด (หมายเหตุ: ใช้การเรียงลำดับหากมีการระบุ) และเมื่อนั้นเท่านั้นจึงจะสามารถส่งคืนบันทึกตั้งแต่ n+1 เป็นต้นไป สิ่งที่น่าสนใจที่สุดคือปัญหาไม่ได้อยู่ในการใช้งานเฉพาะใน DBMS แต่อยู่ในคำจำกัดความดั้งเดิมตามมาตรฐาน:

...แถวแรกจะถูกจัดเรียงตาม จากนั้นจำกัดโดยการลดจำนวนแถวที่ระบุใน จากจุดเริ่มต้น...
-SQL:2016 ส่วนที่ 2 4.15.3 ตารางที่ได้รับมา (หมายเหตุ: ปัจจุบันเป็นมาตรฐานที่ใช้มากที่สุด)

จุดสำคัญที่นี่คือออฟเซ็ตใช้พารามิเตอร์ตัวเดียว - จำนวนบันทึกที่จะข้าม ก็แค่นั้นแหละ ตามคำจำกัดความนี้ DBMS จะสามารถดึงข้อมูลบันทึกทั้งหมดเท่านั้น จากนั้นจึงละทิ้งบันทึกที่ไม่จำเป็นออกไป แน่นอนว่าคำจำกัดความของการชดเชยนี้บังคับให้เราทำงานพิเศษ และไม่สำคัญว่าจะเป็น SQL หรือ NoSQL ก็ตาม

เจ็บอีกนิดหน่อยเท่านั้น

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

เหตุใดคุณจึงต้องมีเครื่องมือสนับสนุนสำหรับการแบ่งหน้าบนคีย์

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

ตัวเลขนี้แสดงให้เห็นสถานการณ์นี้อย่างชัดเจน ฐานจะอ่าน 10 ระเบียนแรก หลังจากนั้นจึงแทรกระเบียนใหม่ ซึ่งจะชดเชยระเบียนที่อ่านทั้งหมดด้วย 1 จากนั้นฐานจะใช้หน้าใหม่จาก 10 ระเบียนถัดไป และไม่เริ่มต้นจากวันที่ 11 เท่าที่ควร แต่จาก วันที่ 10 ทำซ้ำบันทึกนี้ มีความผิดปกติอื่น ๆ ที่เกี่ยวข้องกับการใช้สำนวนนี้ แต่นี่เป็นอาการที่พบบ่อยที่สุด

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

นอกจากนี้ ควรชี้แจงด้วยว่านี่ไม่ใช่ปัญหากับคำหลักบางคำ แต่เป็นปัญหาเกี่ยวกับความหมายของข้อความค้นหา มีไวยากรณ์อีกหลายรูปแบบที่เหมือนกันในลักษณะที่เป็นปัญหา:

  • คำสำคัญชดเชยเป็นไปตามที่กล่าวไว้ก่อนหน้านี้
  • การสร้างขีดจำกัดของคำหลักสองคำ [offset] (แม้ว่าตัวจำกัดเองก็ไม่ได้แย่ขนาดนั้น)
  • การกรองตามขอบเขตล่าง ตามการกำหนดหมายเลขแถว (เช่น row_number(), rownum ฯลฯ)

สำนวนทั้งหมดนี้เพียงบอกคุณว่าต้องข้ามกี่บรรทัด โดยไม่มีข้อมูลเพิ่มเติมหรือบริบท

ต่อมาในบทความนี้ คำสำคัญ offset จะถูกใช้เป็นบทสรุปของตัวเลือกเหล่านี้ทั้งหมด

ชีวิตที่ปราศจากออฟเซ็ต

ทีนี้ลองจินตนาการว่าโลกของเราจะเป็นอย่างไรหากปราศจากปัญหาเหล่านี้ ปรากฎว่าชีวิตที่ไม่มีการชดเชยนั้นไม่ใช่เรื่องยาก: ด้วยการเลือก คุณสามารถเลือกเฉพาะแถวที่เรายังไม่ได้เห็น (หมายเหตุ: นั่นคือแถวที่ไม่ได้อยู่ในหน้าก่อนหน้า) โดยใช้เงื่อนไขโดยที่

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

    SELECT ...
    FROM ...
    WHERE ...
    AND id < ?last_seen_id
    ORDER BY id DESC
    FETCH FIRST 10 ROWS ONLY

นั่นคือหลักการทั้งหมดของแนวทางนี้ แน่นอนว่าสิ่งต่างๆ จะสนุกยิ่งขึ้นเมื่อจัดเรียงตามคอลัมน์ต่างๆ แต่แนวคิดยังคงเหมือนเดิม สิ่งสำคัญคือต้องทราบว่าการออกแบบนี้ใช้ได้กับหลาย ๆ คน NoSQL-การตัดสินใจ

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

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

แล้วเครื่องมือล่ะ?

การแบ่งหน้าบนคีย์มักไม่เหมาะสมเนื่องจากขาดเครื่องมือสนับสนุนสำหรับวิธีนี้ เครื่องมือพัฒนาส่วนใหญ่ รวมถึงเฟรมเวิร์กต่างๆ ไม่อนุญาตให้คุณเลือกวิธีดำเนินการแบ่งหน้าอย่างชัดเจน

สถานการณ์เลวร้ายลงจากความจริงที่ว่าวิธีการที่อธิบายไว้นั้นต้องการการสนับสนุนแบบ end-to-end ในเทคโนโลยีที่ใช้ตั้งแต่ DBMS ไปจนถึงการดำเนินการคำขอ AJAX ในเบราว์เซอร์ที่มีการเลื่อนอย่างไม่มีที่สิ้นสุด แทนที่จะระบุเพียงหมายเลขหน้า ตอนนี้คุณต้องระบุชุดคีย์สำหรับเพจทั้งหมดพร้อมกัน

อย่างไรก็ตาม จำนวนเฟรมเวิร์กที่รองรับการแบ่งหน้าบนคีย์กำลังค่อยๆ เพิ่มขึ้น นี่คือสิ่งที่เรามีในขณะนี้:

(หมายเหตุ: ลิงก์บางส่วนถูกลบออกเนื่องจากในขณะที่แปลห้องสมุดบางแห่งไม่ได้รับการอัปเดตตั้งแต่ปี 2017-2018 หากคุณสนใจสามารถดูแหล่งที่มาดั้งเดิมได้)

ในขณะนี้คุณต้องการความช่วยเหลือจากคุณ หากคุณพัฒนาหรือสนับสนุนเฟรมเวิร์กที่ใช้การแบ่งหน้า ฉันขอวิงวอนให้คุณให้การสนับสนุนดั้งเดิมสำหรับการแบ่งหน้าบนคีย์ หากคุณมีคำถามหรือต้องการความช่วยเหลือ เรายินดีที่จะช่วยเหลือ (ฟอรั่ม, Twitter, แบบฟอร์มการติดต่อ) (หมายเหตุ: จากประสบการณ์ของฉันกับ Marcus ฉันสามารถพูดได้ว่าเขากระตือรือร้นที่จะเผยแพร่หัวข้อนี้จริงๆ)

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

ข้อสรุป

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

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

ที่มา: https://use-the-index-luke.com/no-offset
ผู้เขียน : มาร์คุส วินันด์

ที่มา: will.com

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