Chỉ mục (index) trong MySQL: vì sao truy vấn của bạn chậm

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Bảng có vài chục nghìn dòng thì truy vấn nào cũng nhanh. Đến hàng triệu dòng, một câu WHERE thiếu chỉ mục có thể làm cả trang web chậm hẳn. Hiểu chỉ mục là kỹ năng database đáng giá nhất mà một lập trình viên backend có thể học sớm.

Chỉ mục là gì?

Hãy tưởng tượng một cuốn sách dày không có mục lục: tìm một từ phải lật từng trang. Chỉ mục giống mục lục của bảng: một cấu trúc dữ liệu riêng (thường là B-tree) giúp database nhảy thẳng tới các dòng phù hợp thay vì quét toàn bộ bảng (full table scan).

Đổi lại, chỉ mục chiếm dung lượng và làm thao tác ghi (INSERT, UPDATE, DELETE) chậm hơn một chút, vì phải cập nhật cả chỉ mục. Vì vậy không nên đánh chỉ mục cho mọi cột.

Tạo chỉ mục

CREATE TABLE orders (  id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,  user_id BIGINT UNSIGNED NOT NULL,  status VARCHAR(20) NOT NULL,  created_at DATETIME NOT NULL);CREATE INDEX idx_orders_user ON orders (user_id);CREATE INDEX idx_orders_user_created ON orders (user_id, created_at);CREATE UNIQUE INDEX uq_users_email ON users (email);

Khóa chính tự động có chỉ mục. Chỉ mục duy nhất (UNIQUE) vừa tăng tốc tìm kiếm, vừa đảm bảo không có giá trị trùng.

Dùng EXPLAIN để biết thật sự chuyện gì xảy ra

EXPLAIN SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20;

Hãy chú ý vài cột trong kết quả:

  • type: ALL nghĩa là quét toàn bộ bảng (thường là điều cần tránh trên bảng lớn); ref, range, const là tốt hơn.
  • key: chỉ mục thực sự được dùng.
  • rows: số dòng ước tính database phải xem.
  • Extra: Using filesort hay Using temporary là dấu hiệu cần cân nhắc.

Chỉ mục kết hợp và quy tắc tiền tố trái

Chỉ mục trên nhiều cột (user_id, created_at) hoạt động theo thứ tự cột từ trái sang phải. Nó giúp cho truy vấn lọc theo user_id, hoặc theo cả user_id và created_at, nhưng không giúp được truy vấn chỉ lọc theo created_at. Đây gọi là quy tắc tiền tố trái (leftmost prefix).

Nguyên tắc chọn thứ tự cột: đặt cột dùng so sánh bằng (=) trước, rồi tới cột dùng để lọc khoảng hoặc sắp xếp. Với truy vấn ở trên, chỉ mục (user_id, created_at) vừa lọc theo người dùng vừa cho sẵn thứ tự theo thời gian, nên tránh được bước sắp xếp thêm.

Những lý do chỉ mục không được dùng

  • Bọc cột trong hàm: WHERE YEAR(created_at) = 2024 vô hiệu hóa chỉ mục; hãy viết created_at >= '2024-01-01' AND created_at < '2025-01-01'.
  • Dấu % ở đầu: LIKE '%abc' không dùng được chỉ mục; LIKE 'abc%' thì được.
  • Sai kiểu dữ liệu: so sánh cột chuỗi với số có thể khiến MySQL ép kiểu và bỏ qua chỉ mục.
  • Cột có độ phân biệt thấp: cột chỉ có vài giá trị (như giới tính) ít khi đáng đánh chỉ mục riêng.
  • Quá nhiều dòng trùng khớp: nếu truy vấn lấy phần lớn bảng, quét toàn bộ có thể nhanh hơn.

Covering index

Khi mọi cột mà truy vấn cần đều nằm trong chỉ mục, MySQL không cần đọc bản ghi gốc, gọi là covering index và rất nhanh. Vì vậy hạn chế dùng SELECT * khi chỉ cần vài cột, và đôi khi thêm cột vào chỉ mục để "bao phủ" những truy vấn nóng.

Quy trình tối ưu thực tế

  1. Bật slow query log để tìm các truy vấn chậm thật sự thay vì đoán.
  2. Dùng EXPLAIN để xem kế hoạch thực thi.
  3. Thêm hoặc điều chỉnh chỉ mục, rồi đo lại.
  4. Kiểm tra tác động lên thao tác ghi và dung lượng.
  5. Xóa chỉ mục thừa không còn được dùng.

Chỉ mục và khóa ngoại

Khóa ngoại giúp bảo toàn tính toàn vẹn dữ liệu, và InnoDB tự tạo chỉ mục trên cột khóa ngoại nếu chưa có. Dù vậy, hãy chủ động kiểm tra: các cột dùng trong JOIN và trong mệnh đề WHERE thường xuyên nên được đánh chỉ mục. Khi thiếu chỉ mục ở bảng được JOIN, một truy vấn nhìn có vẻ vô hại cũng có thể buộc MySQL quét nhiều lần cả bảng lớn. Sau mỗi lần thay đổi cấu trúc, hãy chạy lại EXPLAIN cho các truy vấn quan trọng để chắc chắn kế hoạch thực thi vẫn như mong đợi.

Kết luận

Chỉ mục không phải phép màu, nhưng khi dùng đúng chúng biến truy vấn từ vài giây xuống vài mili giây. Hãy tập thói quen: viết truy vấn, chạy EXPLAIN, và hỏi "chỉ mục nào phục vụ câu này?". Chỉ riêng thói quen đó đã giúp bạn tránh được phần lớn lỗi hiệu năng database.

Khi cần một lộ trình học có hệ thống, bạn có thể tham khảo khóa học trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment