Giao diện
Query Optimization Strategies: Tuyệt kỹ
Dưới đây là những kỹ thuật tối ưu hóa thực chiếnh mà bạn có thể áp dụng ngay để tăng tốc hệ thống.
1. Vấn đề N+1 Query (The Silent Killer)
Đây là lỗi phổ biến nhất khi dùng ORM (như Hibernate, Entity Framework, Laravel Eloquent).
Tình huống: Bạn muốn lấy danh sách Users và Orders của họ.
python
users = User.objects.all() # 1 Query lấy Users
for user in users:
print(user.orders) # N Query lấy Orders cho từng UserNếu có 1000 users, bạn sẽ chạy 1 + 1000 = 1001 query. Database sẽ bị "ngập lụt".
Giải pháp (Eager Loading): Dùng JOIN ngay từ đầu để lấy dữ liệu trong 1 query duy nhất.
sql
-- ORM sẽ sinh ra câu này
SELECT * FROM users
LEFT JOIN orders ON users.id = orders.user_id;2. Tối ưu hóa điều kiện OR
Database thường rất dở trong việc dùng Index cho điều kiện OR.
Query chậm:
sql
SELECT * FROM users WHERE email = 'abc@gmail.com' OR phone = '0901234567';
-- Thường sẽ dẫn đến Full Table Scan nếu Database không thông minh.Giải pháp (UNION ALL): Tách thành 2 query nhỏ dùng Index riêng biệt và gộp lại.
sql
(SELECT * FROM users WHERE email = 'abc@gmail.com')
UNION ALL
(SELECT * FROM users WHERE phone = '0901234567');Mỗi query con sẽ dùng Index của email và phone cực nhanh.
3. Chỉ lấy những gì bạn cần (Select Fields)
Như đã nói ở bài Basic, đừng bao giờ SELECT *. Ngoài ra, hãy cẩn thận với cột kiểu TEXT, JSON lớn. Nếu không cần hiển thị nội dung bài viết, đừng SELECT content. Việc chuyển dữ liệu lớn qua mạng (Network I/O) thường chậm hơn bạn nghĩ.
4. Đừng tính toán trong Database nếu không cần thiết
Database được thiết kế để lưu trữ và truy xuất, không phải để tính toán phức tạp.
Tệ:
sql
-- Bắt Database tính MD5 cho 1 triệu dòng
SELECT id, MD5(email) FROM users;Tốt: Lấy email ra và để Application Server (Node.js, Go, Java) tính MD5. App Server scale (mở rộng) dễ hơn và rẻ hơn Database Server rất nhiều.
5. Bulk Insert / Bulk Update
Đừng bao giờ insert dữ liệu trong vòng lặp for.
Sai (Chậm gấp 100 lần):
python
for item in items:
db.insert(item) -- Mỗi lần 1 kết nối, 1 transactionĐúng:
sql
INSERT INTO table (col1, col2) VALUES
(1, 'a'),
(2, 'b'),
(3, 'c'); -- 1 Query duy nhất🧠 Quiz
Câu 1: N+1 Query Problem là gì?
- [ ] A) Query trả về N+1 cột
- [x] B) 1 query lấy danh sách (N items), rồi N query riêng lẻ lấy chi tiết mỗi item — tổng N+1 queries thay vì 1-2
- [ ] C) Query chạy N+1 lần do retry
- [ ] D) Bảng có N+1 index
💡 Giải thích: Ví dụ: SELECT orders (1 query) rồi for each order, SELECT items (N queries). Tổng: N+1 round trips. Fix: dùng JOIN hoặc WHERE id IN (...) để lấy tất cả items trong 1-2 query. ORM thường gây N+1 nếu không dùng eager loading.
Câu 2: Tại sao SELECT col1, col2 tốt hơn SELECT * về mặt optimization?
- [ ] A) Cú pháp đẹp hơn
- [x] B) Database chỉ đọc cột cần thiết, có thể dùng covering index, giảm I/O và network bandwidth
- [ ] C)
SELECT *bị deprecated - [ ] D) Không khác nhau với database hiện đại
💡 Giải thích:
SELECT *trên bảng có blob/text columns = đọc hàng MB dữ liệu không cần. Chọn cụ thể cột cho phép Index-Only Scan nếu index cover đủ cột, giảm disk I/O và network transfer đáng kể.
Câu 3: UNION ALL khác UNION khi optimize OR conditions?
- [x] A) UNION ALL không loại bỏ duplicate nên nhanh hơn — dùng khi biết chắc không có trùng lặp giữa các phần
- [ ] B) UNION ALL chậm hơn UNION
- [ ] C) Chỉ UNION dùng được với index
- [ ] D) UNION ALL giới hạn số dòng trả về
💡 Giải thích:
WHERE a=1 OR b=2thường gây full scan (2 index khó dùng cùng lúc). Tách thànhSELECT ... WHERE a=1 UNION ALL SELECT ... WHERE b=2cho phép mỗi phần dùng index riêng. UNION ALL bỏ qua bước sort/distinct nên nhanh hơn UNION.