Lý do nằm trong việc sử dụng HOẶC điều kiện trong WHERE mệnh đề.
Để minh họa, hãy thử chạy lại truy vấn, lần này chỉ với id = 5
điều kiện và nhận (đầu ra EXPLAIN):
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 1 | PRIMARY | tree | const | PRIMARY,index_both | PRIMARY | 4 | const | 1 | |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+
Và một lần nữa, lần này chỉ với parent_id = @last_id OR parent_id = 5
điều kiện và nhận được:
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
| 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | |
| 1 | PRIMARY | tree | ALL | index_parent_id | NULL | NULL | NULL | 10 | Using where |
| 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+
MySQL không quá tốt với việc xử lý nhiều chỉ mục trong cùng một truy vấn. Mọi thứ tốt hơn một chút với điều kiện AND; một người có nhiều khả năng thấy index_merge tối ưu hóa hơn liên kết chỉ mục tối ưu hóa.
Mọi thứ đang được cải thiện khi các phiên bản nâng cấp, nhưng tôi đã kiểm tra truy vấn của bạn trên phiên bản 5.5
, đây là phiên bản sản xuất mới nhất hiện tại và kết quả như bạn mô tả.
Để giải thích tại sao điều này lại khó, hãy xem xét:hai chỉ mục khác nhau sẽ trả lời cho hai điều kiện khác nhau của truy vấn. Một người sẽ trả lời cho id = 5
, cái còn lại cho parent_id = @last_id OR parent_id = 5
(BTW không có vấn đề gì với HOẶC bên trong cái thứ hai, vì cả hai thuật ngữ đều được xử lý từ trong cùng một chỉ mục).
Không có chỉ mục duy nhất nào có thể trả lời cho cả hai, và do đó, FORCE INDEX
hướng dẫn bị bỏ qua. Xem, FORCE INDEX
cho biết MySQL phải sử dụng an lập chỉ mục qua quá trình quét bảng. Nó không có nghĩa là nó phải sử dụng nhiều hơn một chỉ mục trong quá trình quét bảng.
Vì vậy, MySQL tuân theo các quy tắc của tài liệu ở đây. Nhưng tại sao điều này lại phức tạp như vậy? Bởi vì để trả lời bằng cách sử dụng cả hai chỉ mục, MySQL phải thu thập kết quả từ cả hai, lưu trữ một bên trong một bộ đệm tạm thời nào đó trong khi quản lý phần thứ hai. Sau đó, phải đi qua bộ đệm đó để lọc ra các hàng giống hệt nhau (có thể một số hàng phù hợp với tất cả các điều kiện). Và sau đó quét bộ đệm đó để trả về kết quả.
Nhưng chờ đã, bản thân bộ đệm đó không được lập chỉ mục. Lọc các bản sao không phải là một nhiệm vụ hiển nhiên. Vì vậy, MySQL thích làm việc trên bảng gốc và thực hiện quét ở đó, và tránh tất cả những thứ lộn xộn đó.
Tất nhiên điều này có thể giải quyết được. Các kỹ sư tại Oracle có thể vẫn chưa cải thiện điều này (gần đây họ đã làm việc chăm chỉ để cải thiện các kế hoạch thực thi truy vấn), nhưng tôi không biết liệu đây có phải là nhiệm vụ CẦN LÀM hay nó có mức độ ưu tiên cao hay không.