การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี
เหตุผลที่ตัววางแผนเลือกแต่ละแบบ และสิ่งที่ตัวเลือกนั้นบอกเกี่ยวกับคำสั่งค้นหาของคุณ
การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี เป็นบทเรียน SQL Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน SQL Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
สามวิธีในการอ่านตาราง
เมื่อตัววางแผนต้องการแถวจากตาราง ตัววางแผนจะเลือกวิธีการเข้าถึงหนึ่งในสามวิธี และผู้สัมภาษณ์คาดหวังให้คุณบอกได้ครบทั้งสามวิธี:
- การสแกนตามลำดับ อ่านทุกแถวในตารางตั้งแต่ต้นจนจบ
- การสแกนดัชนี เดินไปตามดัชนีเพื่อค้นหาแถวที่ตรงกัน แล้วดึงแต่ละแถวจากตาราง
- การสแกนเฉพาะดัชนี ตอบคำถามได้จากดัชนีทั้งหมดโดยไม่แตะตารางเลย
การรู้ว่าเหตุใดตัววางแผนจึงเลือกแต่ละวิธีคือหัวใจของบทเรียนนี้ และเป็นคำถามระดับผู้มีประสบการณ์สูงที่มักถูกถามอย่างแน่นอน
การสแกนตามลำดับทำอะไร
การสแกนตามลำดับจะอ่านหน้าของตารางทีละหน้าต่อกัน และใช้ตัวกรองกับแต่ละแถว ไม่มีการใช้ดัชนี
ฟังดูไม่ดี แต่บ่อยครั้งกลับเป็นตัวเลือกที่ถูกต้อง การอ่านตามลำดับทำได้รวดเร็วสำหรับดิสก์ เพราะไม่ต้องกระโดดแบบสุ่ม ดังนั้นเมื่อคำสั่งสอบถามส่งคืนแถวเป็นสัดส่วนมากของตาราง การอ่านทุกอย่างจึงดีกว่าการกระโดดผ่านดัชนีนับล้านครั้ง
ตัวอย่างคือ สแกน orders แล้วเก็บแถวที่ amount > 100 หากคำสั่งซื้อส่วนใหญ่มีจำนวนเงินเกิน 100 การสแกนตามลำดับก็ถูกต้อง
EXPLAIN SELECT * FROM orders WHERE amount > 100;
Seq Scan on orders (cost=0.00..18334.00 rows=900000 width=64)
Filter: (amount > 100)การสแกนดัชนีทำอะไร
การสแกนดัชนีใช้ต้นไม้ B เพื่อกระโดดตรงไปยังคีย์ที่ตรงกัน แล้วอ่านแถวที่เกี่ยวข้องจากฮีปของตาราง
วิธีนี้โดดเด่นเมื่อ ตัวกรองมีความจำเพาะสูงและส่งคืนแถวเพียงส่วนน้อยของตาราง การค้นหา 5 แถวผ่านดัชนีย่อมดีกว่าการอ่าน 10 ล้านแถว
แผนจะระบุชื่อดัชนีที่ใช้ การจับคู่แต่ละครั้งมีค่าใช้จ่ายเป็นการค้นหาดัชนีหนึ่งครั้งและการดึงข้อมูลจากฮีปหนึ่งครั้ง (การอ่านแบบสุ่ม) ดังนั้นการสแกนดัชนีจึงเสียข้อได้เปรียบเมื่อส่งคืนแถวมากเกินไป
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;
Index Scan using idx_orders_customer on orders
(cost=0.42..38.50 rows=12 width=64)
Index Cond: (customer_id = 42)ความจำเพาะในการเลือกเป็นตัวตัดสิน
แนวคิดเดียวที่เป็นตัวขับเคลื่อนเรื่องทั้งหมดนี้คือความจำเพาะในการเลือก: สัดส่วนของแถวที่เพรดิเคตเก็บไว้
- ความจำเพาะสูง (มีแถวตรงกันน้อย เช่น รหัสเฉพาะ) เหมาะกับการสแกนดัชนี
- ความจำเพาะต่ำ (มีแถวตรงกันมาก เช่น
status IS NOT NULL) เหมาะกับการสแกนตามลำดับ
กฎโดยคร่าว ๆ คือ เมื่อคำสั่งสอบถามส่งคืนแถวมากกว่าประมาณ 5 ถึง 10 เปอร์เซ็นต์ของตาราง ตัววางแผนมักเลือกการสแกนตามลำดับ เพราะการดึงข้อมูลจากฮีปแบบสุ่มของดัชนีมีค่าใช้จ่ายสูงกว่าการอ่านทุกอย่างตามลำดับ
การสแกนเฉพาะดัชนี
การสแกนเฉพาะดัชนีเป็นวิธีที่เร็วที่สุดในสามวิธี หากทุกคอลัมน์ที่คำสั่งสอบถามต้องใช้มีอยู่ในดัชนีแล้ว ระบบฐานข้อมูลจะไม่แตะฮีปของตารางเลย
คำสั่งสอบถามตัวอย่างเลือกเฉพาะ customer_id และกรองด้วยคอลัมน์นี้ โดยมีดัชนีบน customer_id ข้อมูลที่ต้องใช้ทั้งหมดอยู่ในดัชนี ดังนั้น Postgres จึงรายงานว่าเป็นการสแกนเฉพาะดัชนี
วิธีนี้หลีกเลี่ยงการอ่านฮีปแบบสุ่มที่ทำให้การสแกนดัชนีทั่วไปช้าลง จึงได้ประโยชน์อย่างมากกับตารางที่มีขนาดแถวใหญ่
EXPLAIN SELECT customer_id FROM orders WHERE customer_id = 42;
Index Only Scan using idx_orders_customer on orders
(cost=0.42..8.44 rows=12 width=4)
Index Cond: (customer_id = 42)ข้อจำกัดของแผนผังการมองเห็น
ผู้สัมภาษณ์ชอบรายละเอียดนี้มาก การสแกนเฉพาะดัชนียังคงต้องยืนยันว่าแต่ละแถวมองเห็นได้สำหรับธุรกรรมของคุณ (MVCC) และดัชนีเพียงอย่างเดียวไม่ได้เก็บข้อมูลการมองเห็นไว้
Postgres ใช้แผนผังการมองเห็น: หากหน้าหนึ่งถูกทำเครื่องหมายว่ามองเห็นทั้งหมด ระบบจะข้ามฮีป แต่หากไม่ใช่ ระบบก็ต้องดึงแถวจากฮีปอยู่ดี แผนจะแสดง Heap Fetches: N
นี่คือเหตุผลที่ตารางซึ่งเพิ่งได้รับการอัปเดตอาจแสดงการดึงข้อมูลจากฮีปจำนวนมาก และทำให้การสแกนเฉพาะดัชนีช้าลง จนกว่า VACUUM จะปรับปรุงแผนผังการมองเห็น
Index Only Scan using idx_orders_customer on orders
(actual time=0.01..0.03 rows=12 loops=1)
Heap Fetches: 0การสแกนแบบบิตแมป: ทางสายกลาง
มีวิธีที่สี่ซึ่งมักปรากฏในแผน นั่นคือการสแกนฮีปแบบบิตแมป ตัววางแผนเลือกวิธีนี้เมื่อเพรดิเคตตรงกับแถวมากกว่าจำนวนที่การสแกนดัชนีแบบปกติต้องการ แต่น้อยกว่าจำนวนที่ต้องอ่านทั้งตาราง
ขั้นแรก ระบบจะสร้างบิตแมปของตำแหน่งแถวที่ตรงกันจากดัชนี (การสแกนดัชนีแบบบิตแมป) จากนั้นจึงดึงหน้าฮีปตามลำดับทางกายภาพแทนการดึงแบบสุ่ม การดึงตามลำดับมีค่าใช้จ่ายถูกกว่าการอ่านแบบกระจัดกระจายของการสแกนดัชนีทั่วไปมาก
Bitmap Heap Scan on orders (cost=12.0..520.0 rows=8000)
Recheck Cond: (status = 'pending')
-> Bitmap Index Scan on idx_orders_status
(cost=0..12 rows=8000)
Index Cond: (status = 'pending')เหตุผลที่ตัววางแผนไม่ใช้ดัชนีของคุณ
คำถามสัมภาษณ์ที่พบบ่อยคือ: ฉันเพิ่มดัชนีแล้ว แต่แผนยังคงทำการสแกนตามลำดับ ทำไม? เหตุผลที่พบบ่อยมีดังนี้:
- เพรดิเคตมีความจำเพาะไม่สูง การสแกนจึงมีค่าใช้จ่ายถูกกว่าอย่างแท้จริง
- ฟังก์ชันครอบคอลัมน์:
WHERE lower(email) = ...ไม่สามารถใช้ดัชนีทั่วไปบนemailได้ - ชนิดข้อมูลไม่ตรงกัน ทำให้เกิดการแปลงชนิดข้อมูลโดยนัย ซึ่งขัดขวางการใช้ดัชนี
- สถิติไม่เป็นปัจจุบัน ให้เรียกใช้
ANALYZE - ตารางมีขนาดเล็กมาก การสแกนไม่กี่หน้ามีค่าใช้จ่ายถูกว่าค่าใช้จ่ายส่วนเกินของดัชนี
ตัวอย่างการวินิจฉัย
สมมติว่า orders มีดัชนีบน created_at แต่คำสั่งสอบถามนี้ยังคงสแกนตามลำดับ:
สาเหตุคือ DATE(created_at) การครอบคอลัมน์ด้วยฟังก์ชันทำให้ไม่สามารถใช้ดัชนีบนคอลัมน์เดิมได้ วิธีแก้คือเขียนใหม่เป็นเพรดิเคตแบบช่วงที่ปล่อยคอลัมน์ไว้โดยไม่มีฟังก์ชันครอบ หรือสร้างดัชนีนิพจน์บน DATE(created_at)
-- Slow: function on the indexed column
WHERE DATE(created_at) = '2026-01-01'
-- Fast: bare column, range uses the index
WHERE created_at >= '2026-01-01'
AND created_at < '2026-01-02'การเปรียบเทียบวิธีการ
จำการเปรียบเทียบนี้ไว้สำหรับการสัมภาษณ์:
- การสแกนตามลำดับ เหมาะที่สุดเมื่อส่งคืนแถวเป็นสัดส่วนมาก ใช้อินพุต/เอาต์พุตตามลำดับ
- การสแกนดัชนี เหมาะที่สุดสำหรับการค้นหาที่เลือกเฉพาะ ใช้การเดินตามดัชนีร่วมกับการดึงข้อมูลจากฮีปแบบสุ่ม
- การสแกนฮีปแบบบิตแมป เหมาะกับจำนวนแถวที่ตรงกันระดับกลาง ใช้ดัชนีสร้างบิตแมป แล้วอ่านฮีปตามลำดับ
- การสแกนเฉพาะดัชนี เร็วที่สุดเมื่อดัชนีครอบคลุมทุกคอลัมน์ที่ต้องใช้ และทุกหน้ามองเห็นได้ทั้งหมด
ตัววางแผนเลือกวิธีโดยพิจารณาต้นทุนที่ประมาณการไว้ ซึ่งขับเคลื่อนหลัก ๆ ด้วยความจำเพาะในการเลือกและสถิติ
บังคับการทดสอบ (และเหตุผลที่ไม่ควรใช้ในระบบจริง)
เพื่อพิสูจน์ประเด็นหนึ่งในการพัฒนา คุณสามารถชักนำตัววางแผนชั่วคราวได้: SET enable_seqscan = off; จะบังคับให้ตัววางแผนเลือกดัชนีเป็นหลัก เพื่อให้คุณเปรียบเทียบแผนได้
นี่เป็นเทคนิคเพื่อการวินิจฉัยเท่านั้น ไม่ใช่วิธีแก้สำหรับระบบจริง ในการสัมภาษณ์ควรกล่าวว่าวิธีแก้ที่แท้จริงคือการใช้สถิติที่ดีขึ้น ดัชนีที่เหมาะสม หรือการเขียนเพรดิเคตใหม่ ไม่ใช่การปิดใช้คุณลักษณะของตัววางแผนทั่วทั้งระบบ
SET enable_seqscan = off;
EXPLAIN ANALYZE SELECT * FROM orders WHERE amount > 100;
SET enable_seqscan = on;ตรวจสอบอย่างรวดเร็ว
คำสั่งสอบถามเลือกเฉพาะ email และกรองด้วย email โดยมีดัชนีต้นไม้ B บน email แผนแสดง Index Only Scan เหตุใดวิธีนี้จึงเร็วกว่าการสแกนดัชนีทั่วไป?
ทบทวน
ประเด็นสำคัญเกี่ยวกับวิธีการเข้าถึง:
- การสแกนตามลำดับเหมาะกับคำสั่งสอบถามที่มีความจำเพาะต่ำ ส่วนการสแกนดัชนีเหมาะกับคำสั่งสอบถามที่เลือกเฉพาะ
- การสแกนเฉพาะดัชนีหลีกเลี่ยงฮีปได้เมื่อดัชนีครอบคลุมทุกคอลัมน์ที่ต้องใช้ ให้สังเกต
Heap Fetchesและแผนผังการมองเห็น - การสแกนฮีปแบบบิตแมปเป็นทางสายกลาง โดยดึงหน้าฮีปตามลำดับทางกายภาพ
- ตัววางแผนตัดสินใจจากความจำเพาะในการเลือกและสถิติ ฟังก์ชันบนคอลัมน์ ชนิดข้อมูลไม่ตรงกัน และสถิติไม่เป็นปัจจุบัน คือสาเหตุที่ดัชนีถูกละเลย
คำถามที่พบบ่อย
บทเรียน “การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส SQL Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส SQL Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี”
เหตุผลที่ตัววางแผนเลือกแต่ละแบบ และสิ่งที่ตัวเลือกนั้นบอกเกี่ยวกับคำสั่งค้นหาของคุณ คุณปฏิบัติ SQL Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน SQL Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน SQL Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน SQL Interview Prep นี้ได้ไหม
ได้ บทเรียน SQL Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- อ่านแผน EXPLAIN
- การสแกนตามลำดับเทียบกับการสแกนดัชนีและการสแกนเฉพาะดัชนี
- อัลกอริทึมการเชื่อมตาราง: ลูปซ้อน แฮช และผสาน
- ค้นหาและแก้ไขคำสั่งค้นหาที่ช้า