Index-Only Query và Covering Index - Không cần chạm vào bảng dữ liệu

Nếu TẤT CẢ các cột query cần đều nằm trong index, database bỏ qua hoàn toàn bước nhảy vào bảng (nơi phát sinh random I/O tốn kém).

Ví dụ

-- Bảng sessions(id, token, user_id, created_at, data, ...)
-- Query xác thực: tìm user_id theo token
SELECT user_id FROM sessions WHERE token = 'abc123';
 
-- Luồng thông thường (index chỉ có (token)):
--   Index → tìm row_ptr → nhảy tới bảng → đọc TOÀN BỘ dòng
--   → lãng phí: đọc cả cột data (có thể rất lớn) chỉ để lấy user_id
 
-- Luồng index-only (index (token, user_id)):
--   Index → tìm token='abc123' → user_id = 42 nằm NGAY TRONG index
--   → Trả về 42, XONG. Không chạm vào bảng!

Ứng dụng tuyệt vời nhất: bảng quan hệ nhiều-nhiều

-- Bảng user_roles(user_id, role_id): chỉ có 2 cột
-- Tạo 2 index bao phủ cả 2 chiều truy vấn:
CREATE INDEX idx_user_roles ON user_roles (user_id, role_id);
CREATE INDEX idx_role_users ON user_roles (role_id, user_id);
 
SELECT role_id FROM user_roles WHERE user_id = 42;  -- index-only ✓
SELECT user_id FROM user_roles WHERE role_id = 1;   -- index-only ✓
-- Cả hai query KHÔNG BAO GIỜ chạm vào bảng dữ liệu

Mệnh đề INCLUDE (PostgreSQL)

Thêm cột “đi kèm” mà không ảnh hưởng cấu trúc/thứ tự index:

CREATE INDEX ON invoices (customer_id, year) INCLUDE (price);
-- Sắp xếp CHỈ theo customer_id và year
-- price được lưu kèm nhưng KHÔNG ảnh hưởng thứ tự
-- Giữ nguyên ràng buộc UNIQUE nếu có (thêm price không phá unique)
-- Vẫn cho phép index-only query khi cần đọc price
 
SELECT customer_id, year, SUM(price)
FROM invoices WHERE customer_id = 42
GROUP BY customer_id, year;  -- → Không cần chạm bảng invoices

MySQL không hỗ trợ INCLUDE - muốn hiệu ứng tương tự phải thêm cột vào cuối index bình thường (cẩn thận với ràng buộc UNIQUE).

Quy tắc nhớ

Dùng index-only query cho query đơn giản nhưng chạy rất thường xuyên: xác thực phiên đăng nhập, ánh xạ mã sang tên, bảng trung gian nhiều-nhiều. Đừng lạm dụng: thêm quá nhiều cột vào index làm tăng kích thước và chậm thao tác ghi.

Liên quan