ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี
เพิ่มคอลัมน์ที่จำเป็นเพื่อให้คำสั่งค้นหาไม่ต้องเข้าถึงตารางหลัก
ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
การนึกย้อนถึงการดึงข้อมูลจากฮีป
ก่อนหน้านี้คุณได้เรียนรู้ว่า บีทรีทั่วไปจะเก็บเฉพาะคอลัมน์ที่ทำดัชนีและตัวชี้แถว ดังนั้นหลังจากดัชนีค้นหาแถวที่ตรงกันแล้ว กลไกฐานข้อมูลยังต้องไปที่ตารางเพื่ออ่านคอลัมน์อื่น การไปอ่านตารางต่อนี้คือ การดึงข้อมูลจากฮีป และเป็นต้นทุนที่ ดัชนีครอบคลุมถูกออกแบบมาเพื่อกำจัด
ผู้สัมภาษณ์ถามเรื่องดัชนีครอบคลุมเพื่อดูว่าคุณเข้าใจหรือไม่ว่า เหตุใดดัชนีจึงสามารถตอบคำสั่งค้นหาได้ทั้งหมดโดยไม่ต้องเข้าถึงตาราง
ความหมายของคำว่า ‘ครอบคลุม’
ดัชนีจะ ครอบคลุมคำสั่งค้นหาเมื่อคอลัมน์ทุกคอลัมน์ที่คำสั่งค้นหาต้องใช้ใน SELECT, WHERE, ORDER BY และ GROUP BY มีอยู่ในตัวดัชนีเอง
เมื่อเป็นเช่นนั้น กลไกฐานข้อมูลจะอ่านเฉพาะดัชนีและไม่ต้องไปที่ตารางเลย PostgreSQL เรียกวิธีนี้ว่า การสแกนจากดัชนีเท่านั้น ส่วน SQL Server และระบบอื่น ๆ เรียกว่าดัชนีครอบคลุม ผลที่ได้คืออ่านเพจน้อยลงและทำให้คำสั่งค้นหาเร็วขึ้น
ตัวอย่างแบบลงมือทำ: คำสั่งค้นหาที่ดัชนีครอบคลุม
สมมติว่าคำสั่งค้นหาต้องใช้เพียง customer_id และ order_date ดัชนีผสมที่มีคอลัมน์ทั้งสองนี้พอดีจะมีข้อมูลทุกอย่างที่คำสั่งค้นหาต้องการ จึงสามารถตอบคำสั่งค้นหาได้จากดัชนีเพียงอย่างเดียว
CREATE INDEX idx_orders_cust_date
ON orders (customer_id, order_date);
-- Covered: both selected columns are in the index
SELECT customer_id, order_date
FROM orders
WHERE customer_id = 42;คอลัมน์เพิ่มเติมเพียงคอลัมน์เดียวก็ทำให้ไม่ครอบคลุม
เมื่อเพิ่มคอลัมน์ที่ไม่มีอยู่ในดัชนี การครอบคลุมจะหายไป และกลไกฐานข้อมูลต้องดึงข้อมูลจากฮีปเพื่ออ่านคอลัมน์นั้น
ในกรณีนี้ total ไม่ได้อยู่ในดัชนี ดังนั้นแม้ customer_id จะเป็นตัวนำการค้นหาแบบเจาะจง แต่ทุกแถวที่ตรงกันก็ยังต้องดึงข้อมูลจากฮีปเพื่ออ่าน total
-- NOT covered: total is not in the index, forces heap fetches
SELECT customer_id, order_date, total
FROM orders
WHERE customer_id = 42;คำสั่ง INCLUDE
คุณอาจเพิ่ม total เป็นคอลัมน์คีย์ลำดับที่สี่ได้ แต่หากไม่เคยใช้คอลัมน์นี้กรองหรือเรียงลำดับ ก็จะทำให้สิ้นเปลืองพื้นที่ในลำดับการเรียงของโครงสร้างต้นไม้ เครื่องมือที่เหมาะสมกว่าคือ INCLUDE ซึ่ง PostgreSQL และ SQL Server รองรับ โดยจะเก็บคอลัมน์เพิ่มเติมไว้เฉพาะใน โหนดใบของดัชนีในฐานะข้อมูลประกอบ ไม่ใช่ส่วนหนึ่งของคีย์เรียงลำดับ
ตอนนี้คำสั่งค้นหาจะได้รับการครอบคลุมโดยไม่ทำให้ส่วนที่ใช้ค้นหาของดัชนีมีขนาดใหญ่เกินไป
CREATE INDEX idx_orders_cust_date_inc
ON orders (customer_id, order_date)
INCLUDE (total);
-- Now covered: total is carried in the leaf
SELECT customer_id, order_date, total
FROM orders
WHERE customer_id = 42;คอลัมน์คีย์เทียบกับคอลัมน์ที่รวมไว้
ความแตกต่างที่ชัดเจนและช่วยสร้างความประทับใจให้ผู้สัมภาษณ์:
- คอลัมน์คีย์กำหนดลำดับการเรียงและใช้สำหรับ ค้นหาแบบเจาะจงและสแกนช่วงได้ โดยต้องปฏิบัติตามกฎคำนำหน้าซ้ายสุด
- คอลัมน์ที่รวมไว้จะเก็บไว้เฉพาะในโหนดใบในฐานะข้อมูลเพิ่มเติม ไม่สามารถใช้ค้นหาได้ แต่ช่วยให้ดัชนี ครอบคลุมคำสั่งค้นหาได้มากขึ้น
หลักจำง่าย: คอลัมน์ที่คุณใช้ กรองหรือเรียงลำดับให้ใส่ไว้ในคีย์ ส่วนคอลัมน์ที่คุณใช้เพียง ส่งกลับให้ใส่ไว้ใน INCLUDE
MySQL/InnoDB: ความพิเศษของดัชนีแบบจัดกลุ่ม
แสดงให้เห็นว่าคุณเข้าใจความแตกต่างระหว่างระบบฐานข้อมูล InnoDB (MySQL) จะจัดกลุ่มตารางตาม คีย์หลัก ดัชนีรองจึงมีคอลัมน์คีย์หลักติดมาด้วยโดยปริยาย ดังนั้นดัชนีรองจึงครอบคลุมคำสั่งค้นหาทุกคำสั่งที่เลือกเฉพาะคอลัมน์ในดัชนีและคอลัมน์คีย์หลักได้โดยอัตโนมัติ โดยไม่ต้องใช้คำสั่ง INCLUDE (MySQL ไม่มี INCLUDE)
แนวคิดเรื่องการครอบคลุมเป็นสากล แต่ไวยากรณ์และคอลัมน์ที่ได้มาโดยอัตโนมัติจะแตกต่างกันไปตามกลไกฐานข้อมูล
การตรวจสอบการสแกนจากดัชนีเท่านั้น
พิสูจน์การครอบคลุมด้วย EXPLAIN ใน PostgreSQL โหนดของแผนการทำงานจะระบุว่า การสแกนจากดัชนีเท่านั้น แทนที่จะเป็น Index Scan ให้สังเกต Heap Fetches: 0 ใน EXPLAIN (ANALYZE) ซึ่งเป็นสัญญาณยืนยันว่าไม่มีการเข้าถึงตารางเกิดขึ้น
หากคุณคาดว่าจะเห็นการสแกนจากดัชนีเท่านั้น แต่กลับเห็น Index Scan พร้อมการดึงข้อมูลจากฮีป แสดงว่าคอลัมน์ที่เลือกมีคอลัมน์หนึ่งไม่ได้อยู่ในดัชนี
EXPLAIN (ANALYZE)
SELECT customer_id, order_date, total
FROM orders
WHERE customer_id = 42;
-- Look for: Index Only Scan ... Heap Fetches: 0ข้อควรระวังเรื่องแผนผังการมองเห็นของ PostgreSQL
ประเด็นเล็กน้อยเกี่ยวกับ PostgreSQL ที่ควรตอบเพื่อรับคะแนนพิเศษ: การสแกนจากดัชนีเท่านั้นยังอาจแตะฮีปได้ หากเพจไม่ได้ถูกทำเครื่องหมายว่ามองเห็นทั้งหมดใน แผนผังการมองเห็น หลังจากมีการปรับปรุงข้อมูลจำนวนมาก ให้เรียกใช้ VACUUM เพื่อให้แผนผังการมองเห็นเป็นปัจจุบัน มิฉะนั้นค่า Heap Fetches จะเพิ่มขึ้นและประโยชน์จากการสแกนจากดัชนีเท่านั้นจะลดลง
-- Keeps the visibility map fresh so index-only scans stay heap-free
VACUUM ANALYZE orders;เมื่อไม่ควรสร้างดัชนีครอบคลุมแบบกว้าง
ดัชนีครอบคลุมไม่ได้ไม่มีต้นทุน การใส่หลายคอลัมน์ไว้ใน INCLUDE ทำให้ดัชนีมีขนาด ใหญ่ ใช้แคชมากขึ้น และทำให้การเขียนช้าลง เพราะการเขียนที่เกี่ยวข้องทุกครั้งต้องปรับปรุงดัชนีด้วย ควรกล่าวถึงข้อแลกเปลี่ยนดังนี้:
- เหมาะอย่างยิ่งสำหรับคำสั่งค้นหาเพื่ออ่านที่มีขอบเขตแคบและเกิดขึ้นบ่อย
- ไม่เหมาะสำหรับใช้เป็นที่ทิ้งคอลัมน์ทุกคอลัมน์เผื่อไว้ ‘กรณีจำเป็น’
ทำให้คำสั่งค้นหาที่สำคัญครอบคลุม ไม่ใช่ครอบคลุมทั้งแถว
วิธีอธิบายในการสัมภาษณ์
สรุปได้อย่างชัดเจนดังนี้:
‘ดัชนีครอบคลุมมีคอลัมน์ทุกคอลัมน์ที่คำสั่งค้นหาเข้าถึงอยู่ จึงทำให้กลไกฐานข้อมูลตอบคำสั่งค้นหาได้จากดัชนีเพียงอย่างเดียวด้วยการสแกนจากดัชนีเท่านั้น และไม่ต้องดึงข้อมูลจากฮีป ผมใส่คอลัมน์ที่ใช้ค้นหาไว้ในคีย์ และใส่คอลัมน์ที่ส่งกลับเท่านั้นไว้ใน INCLUDE ตรวจสอบว่าการดึงข้อมูลจากฮีปเป็นศูนย์ด้วย EXPLAIN ANALYZE และทำให้ดัชนีมีขนาดแคบเพื่อรักษาความเร็วในการเขียน’
ตรวจสอบความเข้าใจอย่างรวดเร็ว
วิเคราะห์ว่าดัชนีครอบคลุมหรือไม่ และแต่ละคอลัมน์ควรอยู่ตรงส่วนใด
ทบทวน: ดัชนีครอบคลุม
ประเด็นสำคัญ:
- ดัชนีจะ ครอบคลุมคำสั่งค้นหาเมื่อมีคอลัมน์ทุกคอลัมน์ที่คำสั่งค้นหาต้องใช้ ทำให้สามารถใช้ การสแกนจากดัชนีเท่านั้นโดยไม่ต้องดึงข้อมูลจากฮีป
- คอลัมน์คีย์เป็นตัวขับการค้นหาแบบเจาะจงและต้องปฏิบัติตามกฎคำนำหน้าซ้ายสุด ส่วนคอลัมน์ INCLUDEเป็นข้อมูลประกอบที่อยู่เฉพาะในโหนดใบเพื่อให้ครอบคลุมคำสั่งค้นหา
- ดัชนีรองของ InnoDB จะมีคีย์หลักรวมอยู่ด้วยโดยปริยาย
- ตรวจสอบด้วย
EXPLAIN (ANALYZE)และดูค่าHeap Fetchesสำหรับ PostgreSQL ควรเรียกใช้VACUUMให้เป็นปัจจุบัน - ทำให้ดัชนีครอบคลุมมีขนาด แคบเพื่อรักษาประสิทธิภาพการเขียน
ถัดไป: มุมกลับ เมื่อดัชนีกลับทำให้ประสิทธิภาพแย่ลง
คำถามที่พบบ่อย
บทเรียน “ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี”
เพิ่มคอลัมน์ที่จำเป็นเพื่อให้คำสั่งค้นหาไม่ต้องเข้าถึงตารางหลัก คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม
ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ดัชนี B-Tree และประโยชน์ของดัชนี
- ลำดับคอลัมน์ในดัชนีผสม
- ดัชนีครอบคลุมและการสแกนเฉพาะดัชนี
- เมื่อดัชนีส่งผลเสีย: การเขียนและความสามารถในการเลือกข้อมูล