Range Condition phá vỡ Phễu (Nguyên tắc 4: Quét khi gặp điều kiện phạm vi)

Nguyên tắc hay bị bỏ sót nhất nhưng ảnh hưởng performance rất lớn. Khi gặp range condition (>, <, >=, <=, BETWEEN, LIKE 'abc%'), database chuyển sang chế độ scan - từ lúc đó, phễu KHÔNG thể thu hẹp thêm bằng các cột phía sau.

Tại sao?

Index (country, age, married) với WHERE country='VN' AND age > 28 AND married='yes':

│ VN │ 25 │ no  │
│ VN │ 27 │ yes │
│ VN │ 29 │ no  │  ← age > 28 bắt đầu scan từ đây
│ VN │ 29 │ yes │  ← married='yes' ✅
│ VN │ 31 │ no  │  ← married='no' ❌ (vẫn phải đọc!)
│ VN │ 31 │ yes │  ← ✅
│ VN │ 35 │ no  │  ← ❌ (vẫn phải đọc!)
│ VN │ 42 │ yes │  ← ✅

Sau khi bắt đầu scan, các entry married='no''yes' xen kẽ nhau - database không thể “nhảy qua”, phải đọc từng entry và check. Đọc 6 entries, chỉ giữ 3.

Giải pháp: Equality columns TRƯỚC, Range columns SAU

Đổi thành index (country, married, age):

│ VN │ no  │ ... │  ← married='no' → bỏ qua TOÀN BỘ block!
│ VN │ yes │ 27  │
│ VN │ yes │ 29  │  ← Fast lookup đến đây: country='VN', married='yes', age>28
│ VN │ yes │ 31  │  ← scan
│ VN │ yes │ 42  │  ← scan

Fast Lookup qua 2 bước phễu → scan từ age > 28. Chỉ đọc 3 entries thay vì 6, không cần filter thêm. Với bảng 10 triệu users: index sai scan 500,000 entries rồi lọc còn 250,000 (đọc gấp đôi cần thiết); index đúng scan đúng 250,000.

Khi có NHIỀU range condition

Chỉ MỘT cột range hưởng lợi từ index scan. Cột range thứ hai chỉ dùng để filter:

WHERE country='VN' AND age > 25 AND salary > 20000000
-- Chọn cột nào đặt trước? Cột nào FILTER ĐƯỢC NHIỀU ROW HƠN!
-- Nếu 90% users có age > 25 nhưng chỉ 10% có salary > 20M
-- → Đặt salary trước: (country, salary, age)

Quy tắc vàng cần nhớ

  1. Equality columns trước, Range columns sau
  2. Nhiều range conditions → đặt cột filter được nhiều nhất trước
  3. Sau cột range đầu tiên, các cột tiếp theo chỉ dùng để filter (vẫn hữu ích, nhưng không giới hạn scan range)

Ngoại lệ: MySQL Loose Index Scan / Oracle Skip Scan có thể “nhảy qua” entries trong trường hợp đặc biệt (GROUP BY min/max) - nhưng đừng thiết kế index dựa trên nó.

Liên quan