Câu 1Khi kết quả của EXPLAIN QUERY PLAN thay đổi từ SCAN perf_sales sang SEARCH perf_sales USING INDEX ..., điều đó có nghĩa gì?
Chỉ mục — Composite, Partial, Expression và UNIQUE
Trên bảng perf_sales 50.000 dòng, bạn sẽ tạo chỉ mục với CREATE INDEX, xây chỉ mục composite, chỉ mục partial trên status='pending', chỉ mục expression trên amount/100, và chỉ mục UNIQUE trên sale_id, rồi xem EXPLAIN QUERY PLAN chuyển từ FULL SCAN (SCAN) sang INDEX SCAN (SEARCH).
Bộ dữ liệu cho bài này — bảng perf_sales
Một chỉ mục (index) hoạt động như mục lục ở cuối sách: nó cho phép bạn tìm các dòng có giá trị cột cụ thể một cách nhanh chóng.
Bài này đi qua việc tạo chỉ mục với CREATE INDEX, xây chỉ mục composite, partial, expression và UNIQUE, và dùng EXPLAIN QUERY PLAN để xác nhận chiến lược tìm kiếm thay đổi như thế nào.
CREATE INDEX và EXPLAIN QUERY PLAN — chuyển quét toàn bảng sang tìm kiếm chỉ mục
Tạo chỉ mục với CREATE INDEX tên_chỉ_mục ON bảng(cột);.
Khi bạn dùng một cột có chỉ mục trong điều kiện bằng hoặc phạm vi của WHERE, bộ tối ưu có thể chọn tìm kiếm chỉ mục thay vì quét toàn bảng và nhảy thẳng tới các dòng khớp.
Kiểm tra chiến lược tìm kiếm với EXPLAIN QUERY PLAN.
Thêm tiền tố EXPLAIN QUERY PLAN cho truy vấn của bạn và bạn sẽ thấy kế hoạch về cách các dòng sẽ được tìm thay vì kết quả thật.
Không có chỉ mục bạn sẽ thấy SCAN perf_sales (còn gọi là FULL SCAN — duyệt từng dòng của bảng), và khi chỉ mục có hiệu lực bạn sẽ thấy thứ gì đó như SEARCH perf_sales USING INDEX ... (còn gọi là INDEX SCAN — thu hẹp tìm kiếm với chỉ mục).
Câu chữ chính xác của kế hoạch khác nhau tùy môi trường, nên bài này coi chạy thành công là đáp án đúng và đọc ý nghĩa của kế hoạch trong phần văn bản và giải thích.
-- Kế hoạch khi chưa có chỉ mục (ví dụ chỉ đọc riêng)
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SCAN perf_sales (duyệt từng dòng)
-- Tạo chỉ mục, rồi xem cùng kế hoạch
DROP INDEX IF EXISTS ix_region;
CREATE INDEX ix_region ON perf_sales(region);
EXPLAIN QUERY PLAN
SELECT sale_id, amount FROM perf_sales WHERE region = 'East';
--> SEARCH perf_sales USING INDEX ix_region (region=?)
-- Lưu ý: chạy DROP -> CREATE -> EXPLAIN liền mạch trong cùng một lần thực thi
Chỉ mục composite — chỉ mục nhiều cột cùng nhau
Viết nhiều cột trong ngoặc — CREATE INDEX tên ON bảng(col1, col2); — tạo ra một chỉ mục composite (còn gọi là chỉ mục đa cột).
Nó tăng tốc các truy vấn dùng các điều kiện từ cột dẫn đầu trở đi, như WHERE col1 = ... AND col2 >= ....
Thứ tự cột quan trọng: chỉ mục composite không giúp các truy vấn không dùng cột dẫn đầu của nó trong mệnh đề WHERE.
Nếu mẫu truy cập của bạn cố định — chẳng hạn "bằng trên emp_id và phạm vi trên sale_date" — xây chỉ mục composite theo thứ tự đó cho phép một chỉ mục bao trùm cả hai điều kiện.
-- Chỉ mục composite (ví dụ chỉ đọc riêng)
DROP INDEX IF EXISTS ix_demo;
CREATE INDEX ix_demo ON perf_sales(region, sale_date);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE region = 'East' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING INDEX ix_demo (region=? AND sale_date>?)
-- Thu hẹp theo region (bằng), rồi thu hẹp tiếp sale_date theo phạm vi
Chỉ mục partial và chỉ mục expression
Chỉ mục partial được xây với CREATE INDEX tên ON bảng(cột) WHERE điều_kiện; và chỉ mục một tập con của các dòng trong bảng.
Nếu bạn thường tìm các dòng có giá trị cụ thể như status = 'pending', chỉ mục cho riêng các dòng đó giữ chỉ mục nhỏ và tập trung vào các truy vấn bạn thực sự chạy.
Trong perf_sales, chỉ 5.000 dòng (10% tổng) có status = 'pending', nên chỉ mục partial với WHERE status = 'pending' rất phù hợp.
Chỉ mục expression được xây với CREATE INDEX tên ON bảng(biểu_thức); và chỉ mục kết quả của một biểu thức tính toán thay vì một cột thô.
Dùng nó cho các truy vấn áp dụng cùng biểu thức ở phía tìm kiếm, như WHERE amount / 100 = ....
Một chỉ mục thường trên một cột thường không giúp khi bạn áp dụng hàm hoặc số học lên cột đó, nhưng nếu bạn lập chỉ mục bản thân biểu thức, các tìm kiếm dùng biểu thức khớp có thể dùng nó.
-- Chỉ mục partial (ví dụ chỉ đọc riêng)
DROP INDEX IF EXISTS ix_part;
CREATE INDEX ix_part ON perf_sales(sale_date) WHERE status = 'refunded';
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales
WHERE status = 'refunded' AND sale_date >= '2024-01-01';
--> SEARCH perf_sales USING ... INDEX ix_part (sale_date>?)
-- Chỉ mục expression
DROP INDEX IF EXISTS ix_expr;
CREATE INDEX ix_expr ON perf_sales(qty * amount);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE qty * amount = 100000;
--> SEARCH perf_sales USING ... INDEX ix_expr (<expr>=?)
Chỉ mục UNIQUE — chỉ mục không cho trùng lặp
Chỉ mục UNIQUE — xây với CREATE UNIQUE INDEX tên ON bảng(cột); — tăng tốc tìm kiếm và ngăn các giá trị trùng lặp chèn vào cột.
Nếu bạn thử tạo chỉ mục UNIQUE trên cột đã có dữ liệu trùng lặp, bạn sẽ nhận lỗi UNIQUE constraint failed và chỉ mục không được tạo.
Mặt khác: nếu bạn tạo nó trên một cột có giá trị duy nhất (một khóa chính hoặc khóa kinh doanh duy nhất), bạn được cả ngăn trùng lặp và tìm kiếm bằng nhanh.
Trong perf_sales, product (200 giá trị khác biệt) có nhiều lặp, nên chỉ mục UNIQUE không khả thi ở đó. sale_id, ngược lại, là khóa chính và duy nhất, nên bạn có thể xây một cái.
Bài tập kế tiếp tạo chỉ mục UNIQUE trên sale_id, xác nhận một cột duy nhất chấp nhận chỉ mục và các tìm kiếm bằng trên cột đó dùng nó.
-- Chỉ mục UNIQUE (ví dụ chỉ đọc riêng)
DROP INDEX IF EXISTS ix_u_demo;
CREATE UNIQUE INDEX ix_u_demo ON perf_sales(sale_id);
EXPLAIN QUERY PLAN
SELECT sale_id FROM perf_sales WHERE sale_id = 12345;
-- Thử thêm UNIQUE trên cột có trùng lặp bị từ chối
-- CREATE UNIQUE INDEX ix_bad ON perf_sales(product);
--> UNIQUE constraint failed: perf_sales.product
-- product có 200 giá trị khác biệt lặp lại qua 50.000 dòng, nên không thể UNIQUE
Tips — khi nào thêm chỉ mục
Nguyên tắc chung: chỉ mục thường thắng SCAN khi bạn có thể thu hẹp kết quả về khoảng 15-20% hoặc ít hơn của bảng. Trên ngưỡng đó, bộ tối ưu thường chọn SCAN tự động.
- Thêm: tìm kiếm bằng hoặc phạm vi trên cột của bảng lớn, khóa chính / khóa ngoại / cột UNIQUE, khóa JOIN, cột được tham chiếu bởi ORDER BY / GROUP BY
- Bỏ qua: bảng nhỏ vài nghìn dòng hoặc ít hơn, cột chọn lọc thấp như gender, bảng ghi nhiều, LIKE '%foo%' (wildcard ở đầu không dùng được chỉ mục)
Quy trình cơ bản: kiểm tra kế hoạch SCAN / SEARCH với EXPLAIN QUERY PLAN và thêm chỉ mục chỉ cho các truy vấn chậm.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Truy vấn nào hưởng lợi nhiều nhất từ chỉ mục composite CREATE INDEX ix ON perf_sales(emp_id, sale_date);?
Câu 3Điều gì xảy ra khi bạn chạy CREATE UNIQUE INDEX trên cột product chứa các giá trị trùng lặp?