2011-09-29 27 views
10

Tôi có bảng sau MySQL (giản thể):Tại sao loại bỏ chỉ mục này trong MySQL tăng tốc độ truy vấn của tôi 100x?

CREATE TABLE `track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` varchar(256) NOT NULL, 
    `is_active` tinyint(1) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `is_active` (`is_active`, `id`) 
) ENGINE=MyISAM AUTO_INCREMENT=7495088 DEFAULT CHARSET=utf8 

Cột 'is_active' đánh dấu hàng mà tôi muốn bỏ qua ở hầu hết, nhưng không phải tất cả, các truy vấn của tôi. Tôi có một số truy vấn đọc từng phần trong bảng này theo định kỳ. Một trong số họ trông giống như sau:

SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 

Truy vấn này mất hơn một phút để thực thi. Đây là kế hoạch thực hiện:

> EXPLAIN SELECT id,title from track where (track.is_active=1 and track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 
| 1 | SIMPLE  | t  | ref | PRIMARY,is_active | is_active | 1  | const | 3747543 | Using where | 
+----+-------------+-------+------+----------------+--------+---------+-------+---------+-------------+ 

Bây giờ, nếu tôi yêu cầu MySQL bỏ qua chỉ mục 'is_active', truy vấn diễn ra ngay lập tức.

> EXPLAIN SELECT id,title from track IGNORE INDEX(is_active) WHERE (track.is_active=1 AND track.id > 5580702) ORDER BY id ASC LIMIT 10; 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | PRIMARY  | PRIMARY | 4  | NULL | 1597518 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

Bây giờ, điều thực sự lạ là nếu tôi sử dụng MySQL để sử dụng chỉ mục 'is_active', truy vấn lại một lần nữa xảy ra ngay lập tức!

+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | t  | range | is_active  |is_active| 5  | NULL | 1866730 | Using where | 
+----+-------------+-------+-------+---------------+---------+---------+------+---------+-------------+ 

Tôi không hiểu hành vi này. Trong chỉ mục 'is_active', các hàng phải được sắp xếp theo is_active, theo sau là id. Tôi sử dụng cả hai cột 'is_active' và 'id' trong truy vấn của mình, vì vậy có vẻ như chỉ cần thực hiện một vài bước nhảy quanh cây để tìm ID, sau đó sử dụng các ID đó để truy xuất các tiêu đề từ bảng.

gì đang xảy ra?

EDIT: Thông tin thêm về những gì tôi đang làm:

  • Query bộ nhớ cache bị vô hiệu hóa
  • Chạy TỐI ƯU HÓA TABLE và phân tích TABLE không ảnh hưởng
  • 6.620.372 hàng đã 'is_active' thiết lập là True. 874.714 hàng có 'is_active' được đặt thành False.
  • Sử dụng FORCE INDEX (is_active) một lần nữa tăng tốc truy vấn.
  • Phiên bản MySQL 5.1.54
+2

Bạn đang xóa bộ nhớ cache trước khi đo điểm chuẩn, phải không? – dfb

+0

Đồng thời đảm bảo thống kê bảng là hiện tại và các chỉ mục được xây dựng lại. (Tuy nhiên điều này được thực hiện trong MySQL ;-) –

+0

Điều gì sẽ xảy ra nếu bạn đảo ngược điều kiện WHERE? 'where (track.id> 5580702 và track.is_active = 1)' – EJP

Trả lời

7

Dường như MySQL đang đưa ra quyết định kém về cách sử dụng chỉ mục.

Từ kế hoạch truy vấn đó, nó cho thấy nó có thể đã sử dụng chỉ mục CHÍNH hoặc is_active và nó đã chọn is_active để thu hẹp theo dõi.is_active trước tiên. Tuy nhiên, nó chỉ sử dụng cột đầu tiên của chỉ mục (track.is_active). Điều đó được nó 3747543 kết quả mà sau đó phải được lọc và sắp xếp.

Nếu nó đã chọn chỉ mục CHÍNH, nó sẽ có thể thu hẹp xuống 1597518 hàng bằng cách sử dụng chỉ mục và chúng sẽ được truy lục theo thứ tự track.id, không yêu cầu sắp xếp thêm nữa. Điều đó sẽ nhanh hơn.

Thông tin mới:

Trong trường hợp thứ ba mà bạn đang sử dụng FORCE INDEX, MySQL đang sử dụng chỉ số is_active nhưng bây giờ thay vì chỉ sử dụng cột đầu tiên, nó được sử dụng cả hai cột (xem key_len). Do đó, bây giờ có thể thu hẹp bởi is_active và sắp xếp và lọc theo id bằng cách sử dụng cùng một chỉ mục và vì is_active là một hằng số duy nhất, ORDER BY được đáp ứng bởi cột thứ hai (tức là các hàng từ một nhánh của chỉ mục đã sẵn sàng theo thứ tự được sắp xếp). Điều này có vẻ là một kết quả tốt hơn so với sử dụng CHÍNH - và có lẽ những gì bạn dự định ở nơi đầu tiên, phải không?

Tôi không biết tại sao nó không sử dụng cả hai cột của chỉ mục này mà không có FORCE INDEX, trừ khi truy vấn đã thay đổi một cách tinh tế ở giữa. Nếu tôi không đặt nó xuống MySQL để đưa ra quyết định tồi.

+0

Tất nhiên, nếu bạn biết rõ hơn MySQL, bạn luôn có thể sử dụng [USE INDEX()] (http://dev.mysql.com/doc/refman/5.5/en/index-hints.html) để đề xuất chỉ mục nào nên thích. Bạn cũng có thể thử [ANALYZE TABLE] (http://dev.mysql.com/doc/refman/5.5/en/analyze-table.html) để cung cấp cho MySQL một cơ hội để tự tìm ra nó, đôi khi có thể làm việc . – thomasrutter

+0

Nếu tôi sử dụng FORCE INDEX (is_active) truy vấn xảy ra ngay lập tức (xem các chỉnh sửa gần đây). Bất kỳ ý tưởng? – cwick

+0

Tôi không chắc chắn - có thể là bộ nhớ cache của một số loại? Có thể thêm đầu ra GIẢI THÍCH cho điều đó? Bạn có nhận được cùng một đầu ra, theo cùng một thứ tự không? – thomasrutter

1

Tôi nghĩ rằng tăng tốc là do mệnh đề where của bạn. Tôi giả định rằng nó chỉ lấy một tập nhỏ các hàng trong toàn bộ bảng lớn. Việc quét bảng dữ liệu đã truy xuất được thực hiện nhanh hơn đối với is_active trên tập con nhỏ hơn là thực hiện lọc thông qua tệp chỉ mục lớn. Duyệt qua một chỉ mục cột đơn nhanh hơn nhiều so với việc vượt qua chỉ mục kết hợp.

0

vài điều bạn có thể thử:

  • Do một tối ưu hóa và kiểm tra trên bảng của bạn, vì vậy mysql sẽ tính lại chỉ số đánh giá cao
  • có một cái nhìn tại http://dev.mysql.com/doc/refman/5.1/en/index-hints.html - bạn có thể nói với mysql để lựa chọn bên phải chỉ số trong các trường hợp khác nhau
Các vấn đề liên quan