Database Index - Biến Full Scan Thành Tra Cứu Tức Thì
Câu query chạy mượt re với 1.000 dòng, nhưng tới 1.000.000 dòng là bò luôn. Lý do thường gặp: database đang làm full table scan - quét hết từng dòng để tìm mấy dòng mình cần. Index chính là thứ giải quyết chuyện này.
Không có index: quét hết bảng
SELECT * FROM users WHERE email = '[email protected]';
Không có index trên email, engine phải đọc từng dòng rồi so sánh email. Bảng lớn thì đây là công việc O(n) - càng nhiều dòng càng lâu.
Có index: tra cứu tức thì
CREATE INDEX idx_users_email ON users (email);
Index là cấu trúc đã sắp xếp sẵn (thường là B-tree). Engine giờ đi thẳng tới đúng dòng cần tìm với độ phức tạp khoảng O(log n) - chỉ đọc vài lần thay vì cả triệu lần.
Đọc plan bằng EXPLAIN
EXPLAIN SELECT * FROM users WHERE email = '[email protected]';
type = ALL(kèm "Using where") nghĩa là full scan - chậm.type = refhoặcconstcó liệt kêkeynghĩa là đang xài index - ngon.
Composite index đi theo quy tắc trái-sang-phải
CREATE INDEX idx_orders_user_status ON orders (user_id, status);
Cái này giúp cho WHERE user_id = ? và WHERE user_id = ? AND status = ?, nhưng không giúp cho WHERE status = ? đứng một mình - query phải xài prefix bên trái của mấy cột đã đánh index.
Khi nào index KHÔNG giúp được gì
WHERE YEAR(created_at) = 2026- bọc cột trong hàm là index vô dụng liền. Xài range thay thế:created_at >= '2026-01-01' AND created_at < '2027-01-01'.LIKE '%term'có dấu % ở đầu thì không xài được index thường.- Cột có độ chọn lọc thấp (như kiểu boolean) - đôi khi quét hết còn rẻ hơn xài index.
Rút gọn lại
- Không có index là full scan (O(n)); có index đúng chỗ là tra cứu tức thì (O(log n)).
- Đánh index mấy cột hay filter, join, sort - đừng đánh hết mọi cột (index tốn chi phí ghi và lưu trữ).
- Xài
EXPLAINđể chắc chắn index có thật sự được dùng. - Giữ filter sargable: đừng bao giờ bọc cột đã đánh index trong hàm.
Thử thách
Luyện tập ngay điều vừa học. Viết lời giải, mở gợi ý nếu bí.
Code khởi tạo
SELECT * FROM orders
WHERE YEAR(created_at) = 2026;
Lời giải của bạn
SELECT * FROM orders
WHERE created_at >= '2026-01-01'
AND created_at < '2027-01-01';

