Skip to content

EXPLAIN: Bác sĩ khám bệnh cho Query

Khi query chạy chậm, đừng đoán mò. Hãy hỏi Database xem "Mày đang làm cái quái gì thế?" bằng lệnh EXPLAIN.

Cú pháp (Syntax)

sql
EXPLAIN SELECT * FROM users WHERE id = 100;

Khuyên dùng: EXPLAIN ANALYZE (hoặc EXPLAIN (ANALYZE, BUFFERS) trong Postgres).

  • EXPLAIN: Chỉ hiện kế hoạch dự kiến (không chạy query).
  • EXPLAIN ANALYZE: Chạy query thật và đo thời gian thực tế. Cẩn thận với các lệnh DELETE/UPDATE nhé!

Cách đọc kết quả (Reading the Plan)

Query Plan là một cái cây, đọc từ trong ra ngoài, từ dưới lên trên. Tuy nhiên, có 2 từ khóa quan trọng bạn cần soi ngay lập tức:

1. Seq Scan (Sequential Scan) - 🔴 BAD

Duyệt tuần tự. Database đọc từ dòng 1 đến dòng cuối cùng của bảng.

  • Nếu bảng nhỏ (vài trăm dòng): Không sao cả.
  • Nếu bảng lớn (1 triệu dòng): Thảm họa. Cần đánh Index ngay.

2. Index Scan / Index One Only - 🟢 GOOD

Database dùng Index để nhảy thẳng tới dữ liệu cần tìm. Rất nhanh.

3. Bitmap Heap Scan - 🟡 OK

Kết hợp giữa Index và Scan bảng. Thường xảy ra khi Index lọc được một lượng khá lớn bản ghi, nhưng Database vẫn cần lội vào bảng gốc để lấy thêm dữ liệu.

Ví dụ thực tế: Soi bệnh (Case Study)

Query:

sql
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 55;

Kết quả A (Chưa có Index):

Seq Scan on orders  (cost=0.00..1834.00 rows=12 width=344) (actual time=0.5.15..12.45 rows=5 loops=1)
Filter: (user_id = 55)
Rows Removed by Filter: 99995
  • Phân tích: Nó phải quét 100,000 dòng (Rows Removed by Filter: 99995) chỉ để lấy 5 dòng. Quá phí phạm!

Kết quả B (Đã Index user_id):

Index Scan using idx_orders_user_id on orders (cost=0.29..8.30 rows=5 width=344) (actual time=0.023..0.026 rows=5 loops=1)
  • Phân tích: Thời gian giảm từ 12.45ms xuống 0.026ms. Tăng tốc 478 lần!

⚠️ Cảnh báo

Chi phí (cost) là đơn vị ảo của Database, không phải thời gian giây (time). Khi optimize, hãy nhìn vào actual timebuffers (số trang dữ liệu phải đọc từ đĩa/RAM).