คอลัมน์ที่ไม่มี Index ทำฐานข้อมูลของเราพังได้อย่างไร
แค่ลืมสร้าง Index เพียงจุดเดียว ก็เปลี่ยน API ที่เร็ว 8 ms ให้กลายเป็นฝันร้ายนาน 8 วินาที
เราทำการทดสอบ Load Test ก่อนการปล่อยเวอร์ชันสำคัญ ในเครื่อง Local development ข้อมูล 100 แถวทำงานได้ไม่มีปัญหา แต่ใน Staging เราจำลองผู้ใช้งาน 500 คน กับข้อมูล 1.5 ล้านแถว
ทุกอย่างพังพินาศ
- เวลาตอบสนองของ API พุ่งจาก 45 ms เป็น 8,000 ms
- CPU ของฐานข้อมูลพุ่งสูงถึง 100 %
- Connection pools เต็มจนไม่สามารถใช้งานได้
- Request ต่างๆ เกิด Timeout
ตัวการคือ Query ง่ายๆ ที่ดึงประวัติการสั่งซื้อโดยใช้ user_id และ status
เมื่อรัน EXPLAIN ANALYZE บน PostgreSQL พบว่าเป็นแบบ Sequential Scan เนื่องจากไม่มี Index บน user_id ทำให้ Engine ต้องอ่านข้อมูลทั้งหมด 1.5 ล้านแถวในทุกๆ Request
เมื่อมี 100 concurrent requests ฐานข้อมูลต้องสแกนข้อมูลถึง 150 ล้านแถวพร้อมกัน
การแก้ไขใช้เวลาเพียง 5 นาที เราสร้าง composite index บน (user_id, status, created_at DESC) โดยใช้ CREATE INDEX CONCURRENTLY เพื่อให้ตารางยังคงออนไลน์ใช้งานได้ตามปกติ
ผลลัพธ์:
- เวลาในการ Query ลดลงจาก 8,150 ms เหลือเพียง 0.14 ms
- API latency ลดลงจาก 8 วินาที เหลือเพียง 12 ms
- การใช้งาน CPU ลดลงจาก 100 % เหลือไม่ถึง 8 %
บทเรียนที่ได้รับ:
- การทดสอบบน Local อาจทำให้เข้าใจผิด ข้อมูล 100 แถวไม่ใช่ตัวแทนของข้อมูลหลักล้าน
- อย่าลืมทำ Index ให้กับ Foreign keys เพราะ ORM ส่วนใหญ่มักจะข้ามขั้นตอนนี้ไป
- ให้รัน
EXPLAIN ANALYZEเพราะฐานข้อมูลจะชี้ให้เห็นถึงปัญหาเอง - ปรับแต่ง Query ให้เหมาะสมก่อนที่จะคิดซื้อ Hardware เพิ่ม
อย่าเพิ่งรีบ Scale server ให้เริ่มจากการ Scale query ก่อน
