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).

🧠 Quiz

Câu 1: EXPLAIN vs EXPLAIN ANALYZE — khác nhau thế nào?

  • [ ] A) Không khác nhau
  • [x] B) EXPLAIN chỉ hiện query plan dự kiến (không chạy query); EXPLAIN ANALYZE chạy thật và hiện thời gian thực tế
  • [ ] C) EXPLAIN ANALYZE nhanh hơn
  • [ ] D) EXPLAIN chỉ dùng cho SELECT

💡 Giải thích: EXPLAIN = optimizer's plan (estimated cost/rows). EXPLAIN ANALYZE = chạy query thật, đo actual time, actual rows. Lưu ý: EXPLAIN ANALYZE thực sự execute query — với DELETE/UPDATE, hãy wrap trong transaction và ROLLBACK!

Câu 2: Seq Scan (Sequential Scan) xảy ra khi nào?

  • [ ] A) Chỉ khi bảng không có index
  • [x] B) Khi database quyết định đọc toàn bộ bảng nhanh hơn dùng index — do selectivity thấp, bảng nhỏ, hoặc query không match index
  • [ ] C) Luôn chậm hơn Index Scan
  • [ ] D) Chỉ với SELECT *

💡 Giải thích: Seq Scan không luôn xấu! Bảng 100 dòng thì Seq Scan nhanh hơn nhảy qua index. Query lấy 80% rows thì Seq Scan ít random I/O hơn. Chỉ lo khi Seq Scan trên bảng lớn mà query lẽ ra filter được ít dòng.

Câu 3: Giá trị "cost" trong EXPLAIN có đơn vị gì?

  • [ ] A) Milliseconds
  • [ ] B) Số dòng
  • [x] C) Đơn vị ảo (arbitrary units) của database — không phải thời gian, dùng để so sánh tương đối giữa các plan
  • [ ] D) Bytes

💡 Giải thích: Cost là ước lượng nội bộ dựa trên I/O và CPU. Không phải giây hay ms. Dùng để so sánh giữa các query plan. Khi optimize, nhìn vào actual time (từ EXPLAIN ANALYZE) và buffers (shared hit/read) — đó mới là thực tế.