Database Performance
Query optimization, indexing, and measuring before optimizing.
Database Performance
Database Performance คือการบริหารจัดการทรัพยากรเพื่อให้ระบบสามารถดึงและบันทึกข้อมูลได้อย่างรวดเร็ว รองรับปริมาณการใช้งานที่เพิ่มขึ้น โดยไม่ส่งผลกระทบต่อ User experience
Cheat Sheet: Core Performance Concepts
| Concept | Description |
|---|---|
| Query Optimization | การปรับปรุงคำสั่ง SQL (หรือ ORM query) เพื่อให้ Database ทำงานน้อยที่สุดในการค้นหาผลลัพธ์ที่ต้องการ |
| Caching | การจัดเก็บผลลัพธ์ของ Query ที่ใช้บ่อยหรือข้อมูลที่เปลี่ยนแปลงน้อย ไว้ในระบบที่มีความเร็วสูง (เช่น In-memory database) |
| Connection Pooling | การสร้างและจัดการกลุ่มของ Database connections ไว้ล่วงหน้า เพื่อลด Overhead ในการสร้าง Connection ใหม่ทุกครั้งที่มี Request |
Practical Examples
1. Query Optimization with Indexing
Scenario: ตาราง users มีข้อมูล 10 ล้านเรคคอร์ด การค้นหาผู้ใช้ด้วยอีเมลใช้เวลามากกว่า 3 วินาที
Practice: การใช้คำสั่ง EXPLAIN หรือ EXPLAIN ANALYZE จะพบว่า Database กำลังทำ Sequential Scan (สแกนทุกแถว)
Solution: สร้าง Index บนคอลัมน์ email
CREATE INDEX idx_users_email ON users(email);หลังจากสร้าง Index Database จะสามารถค้นหาข้อมูลผ่าน B-Tree structure ลดเวลาการค้นหาจากระดับวินาทีเหลือเพียงเสี้ยวของมิลลิวินาที (Index Scan)
2. Caching Expensive Queries
Scenario: Dashboard ของระบบต้องแสดงผลสรุปยอดขายรายวัน ซึ่งเป็น Query ที่มีการคำนวณซับซ้อน (Aggregations) และใช้เวลาทำงาน 5 วินาที Practice: แทนที่จะให้ Database คำนวณใหม่ทุกครั้งที่มีผู้ใช้เปิด Dashboard เราสามารถนำ Redis มาประยุกต์ใช้เพื่อทำ Caching Workflow:
- Application ตรวจสอบว่ามีข้อมูลยอดขายใน Redis หรือไม่
- หากมี (Cache Hit): ส่งข้อมูลให้ผู้ใช้ทันที
- หากไม่มี (Cache Miss): ส่ง Query ไปยัง Database, นำผลลัพธ์ที่ได้ไปจัดเก็บใน Redis (กำหนด Time-To-Live หรือ TTL เช่น 10 นาที), และส่งข้อมูลให้ผู้ใช้ Result: ระบบสามารถรองรับผู้ใช้งานพร้อมกันได้จำนวนมากโดยไม่สร้างโหลดให้ Database
3. Connection Pooling for High Concurrency
Scenario: แอปพลิเคชันมี Traffic สูงขึ้นอย่างฉับพลัน (Spike) ทำให้ระบบแสดงข้อความ Error "Too many connections" Practice: Database ส่วนใหญ่มีข้อจำกัดในการรองรับ Concurrent connections การสร้าง Connection ใหม่มี Overhead สูงในระดับ Network และ Authentication Solution: นำ Connection Pooler (เช่น PgBouncer สำหรับ PostgreSQL) มาคั่นกลางระหว่าง Application และ Database
- Application จะทำการเชื่อมต่อไปยัง Connection Pool
- Connection Pool จะบริหารจัดการและหมุนเวียน (Reuse) Connection จริงที่มีต่อ Database
- เมื่อมีการส่ง Request จำนวนมากเกินกว่าที่ตั้งไว้ Request เหล่านั้นจะถูกจัดคิวรอ (Queue) แทนที่จะทำให้ Database ทำงานหนักจนล่ม
4. Avoiding N+1 Query Problem
Scenario: การแสดงรายการบทความพร้อมชื่อผู้เขียนผ่าน ORM Practice: การดึงข้อมูลผิดวิธีอาจทำให้เกิดคำสั่ง SQL จำนวนมาก Bad Practice (N+1):
const posts = await Post.find(); // 1 Query
for (let post of posts) {
const author = await User.findById(post.authorId); // N Queries
}Good Practice (Eager Loading / Joins):
const posts = await Post.find().populate('author'); // 1 Optimized Queryการปรับ Query Shape ด้วย Join หรือ Eager loading ช่วยลดระยะเวลาที่ระบบต้องติดต่อกับ Database ได้อย่างมีนัยสำคัญ