2009-04-07 41 views
18

Tôi đang gặp một số vấn đề biểu diễn khá lớn do việc sử dụng "ORDER BY" - câu lệnh trong mã SQL của tôi.MySQL không sử dụng chỉ mục ("Sử dụng filesort") khi sử dụng ORDER BY

Mọi thứ đều ổn miễn là tôi không sử dụng các câu lệnh BY ORDER trong SQL. Tuy nhiên, khi tôi giới thiệu ORDER BY: s trong mã SQL, mọi thứ đều chậm lại đáng kể do thiếu lập chỉ mục chính xác. Người ta cho rằng việc sửa chữa điều này sẽ là tầm thường, nhưng đánh giá từ các cuộc thảo luận diễn đàn, vv điều này có vẻ là một vấn đề khá phổ biến mà tôi chưa thấy câu trả lời dứt khoát và súc tích cho câu hỏi này.

Câu hỏi: Với bảng sau ...

 
CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value2 (value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

... làm cách nào để tạo chỉ số sẽ được sử dụng khi truy vấn bảng cho một value1 trung cấp, trong khi sắp xếp trên giá trị của giá trị2?

Hiện tại, tìm nạp là OK khi KHÔNG sử dụng mệnh đề ORDER BY.

Xem ra QUERY sau GIẢI THÍCH:

 
OK, when NOT using ORDER BY: 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
 
However, when using ORDER BY I get "Using filesort": 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra      | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where; Using filesort | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 

Một số thông tin bổ sung về nội dung bảng:

 
SELECT MIN(value1), MAX(value1) FROM values_table; 
+---------------+---------------+ 
| MIN(value1) | MAX(value2) | 
+---------------+---------------+ 
|    0 | 4294967295 | 
+---------------+---------------+ 

... 

SELECT MIN(value2), MAX(value2) FROM values_table; 
+---------------+---------------+ 
| MIN(value2) | MAX(value2) | 
+---------------+---------------+ 
|    1 |  953359 | 
+---------------+---------------+ 

Xin vui lòng cho tôi biết nếu có thêm thông tin là cần thiết để trả lời câu hỏi .

Cảm ơn rất nhiều trước!

Update # 1: Thêm một chỉ số tổng hợp mới (ALTER TABLE values_table ADD INDEX (value1, value2);) không giải quyết được vấn đề. Bạn vẫn sẽ nhận được "Sử dụng filesort" sau khi thêm một chỉ mục như vậy.

Cập nhật # 2: Ràng buộc mà tôi không đề cập đến trong câu hỏi là tôi muốn thay đổi cấu trúc của bảng (nói thêm chỉ mục, v.v.) thay đổi truy vấn SQL được sử dụng. Các truy vấn SQL được tạo tự động bằng Hibernate, vì vậy hãy xem xét các truy vấn đó ít nhiều cố định.

+0

Tôi giả sử bạn có nghĩa là value1, value2 trong bản cập nhật của bạn, phải không? – paxdiablo

+0

Đừng bận tâm, điều đó sẽ không hoạt động được vì lời giải thích của @ Quassnoi về phạm vi giá trị1. Nó sẽ làm việc cho một giá trị duy nhất của value1 nhưng tôi đã không đọc câu hỏi đủ tốt. May mắn nhất. – paxdiablo

+0

Bạn có sử dụng các trường trực tiếp trong truy vấn của mình hoặc bạn đang sử dụng các chức năng? Giống như trường dấu thời gian và TUẦN (dấu thời gian). –

Trả lời

19

Bạn không thể sử dụng chỉ mục trong trường hợp này, khi bạn sử dụng điều kiện lọc RANGE.

Nếu bạn muốn sử dụng cái gì đó như:

SELECT * 
FROM values_table this_ 
WHERE this_.value1 = @value 
ORDER BY 
     value2 
LIMIT 10 

, sau đó tạo ra một chỉ số tổng hợp trên (VALUE1, VALUE2) sẽ được sử dụng cho cả lọc và cho đặt hàng.

Nhưng bạn sử dụng điều kiện dao động, đó là lý do tại sao bạn cần phải thực hiện đơn đặt hàng.

chỉ số tổng hợp của bạn sẽ trông như thế này:

 
value1 value2 
----- ------ 
1  10 
1  20 
1  30 
1  40 
1  50 
1  60 
2  10 
2  20 
2  30 
3  10 
3  20 
3  30 
3  40 

, và nếu bạn chọn 12 trong value1, bạn vẫn không nhận được một tập hoàn toàn sắp xếp của value2.

Nếu chỉ số của bạn trên value2 không phải là rất có chọn lọc (.. I e không có nhiều DISTINCT value2 trong bảng), bạn có thể thử:

CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1) 

/* Note the order, it's important */  

SELECT * 
FROM (
     SELECT DISTINCT value2 
     FROM mytable 
     ORDER BY 
       value2 
     ) q, 
     mytable m 
WHERE m.value2 >= q.value2 
     AND m.value2 <= q.value2 
     AND m.value1 BETWEEN 13123123 AND 123123123 

này được gọi là phương pháp SKIP SCAN truy cập. MySQL không hỗ trợ trực tiếp, nhưng nó có thể được mô phỏng như thế này.

Quyền truy cập RANGE sẽ được sử dụng trong trường hợp này, nhưng có thể bạn sẽ không nhận được bất kỳ lợi ích hiệu suất nào trừ khi DISTINCT value2 bao gồm ít hơn khoảng 1% hàng.

Lưu ý sử dụng:

m.value2 >= q.value2 
AND m.value2 <= q.value2 

thay vì

m.value2 = q.value2 

Điều này làm cho MySQL thực hiện RANGE kiểm tra trên mỗi vòng lặp.

+0

+1 để chọn vấn đề phạm vi mà tôi đã bỏ lỡ :-) – paxdiablo

+0

Cảm ơn câu trả lời toàn diện của bạn. Giả sử rằng tôi không thể thay đổi các truy vấn SQL được sử dụng (chúng được tạo tự động bởi Hibernate), bạn có tin rằng điều này không thể giải quyết được (bằng cách thêm vào việc lập chỉ mục tốt hơn)? – knorv

+0

Một câu hỏi khác: Nếu phạm vi truy vấn là vấn đề, làm thế nào đến tất cả mọi thứ có vẻ OK khi không sử dụng ORDER BY? Xin lỗi nếu tôi bỏ lỡ chi tiết này. – knorv

0

Dường như với tôi rằng bạn có hai khóa độc lập độc lập, một cho giá trị1 và một cho giá trị2.

Vì vậy, khi bạn sử dụng khóa giá trị 1 để truy xuất, các bản ghi không nhất thiết phải được trả về theo thứ tự giá trị2, do đó chúng phải được sắp xếp. Điều này vẫn tốt hơn quét toàn bộ bảng vì bạn chỉ phân loại các bản ghi thỏa mãn mệnh đề "where value1" của bạn.

Tôi nghĩ rằng (nếu điều này có thể xảy ra trong MySQL), khóa tổng hợp trên (giá trị1, giá trị2) sẽ giải quyết vấn đề này.

Hãy thử:

CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value1and2 (value1,value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

(hoặc tương đương ALTER TABLE), giả định đó là đúng cú pháp trong MySQL cho một chìa khóa composite.

Trong tất cả các cơ sở dữ liệu tôi biết (và tôi phải thừa nhận MySQL không phải là một trong số chúng), điều đó sẽ làm cho công cụ DB chọn khóa value1and2 để truy xuất các hàng và chúng sẽ được sắp xếp theo giá trị 2 trong thứ tự giá trị1, vì vậy sẽ không cần sắp xếp tệp.

Bạn vẫn có thể giữ khóa giá trị 2 nếu cần.

+0

Xin chào, cảm ơn bạn đã trả lời nhanh chóng. Tôi đã thử giải pháp được đề xuất của bạn và rất tiếc là nó không hoạt động. Tôi đã thêm một giải thích cho câu hỏi của mình. – knorv

+0

Không có probs, có vẻ như @Quassnoi có nhiều kiến ​​thức về MySQL hơn nên tôi sẽ để lại cho bạn. Giải thích của ông về lý do tại sao phân loại là cần thiết cho giá trị tầm xa1 là cái gì tôi đã không nhận được từ câu hỏi - DB2 sẽ có các probs tương tự. Ghi nhận là cộng đồng wiki nên không ai khác mắc lỗi tương tự. – paxdiablo

+0

Có một số loại lỗi SO ngăn cản tôi đánh dấu câu hỏi cộng đồng wiki. – paxdiablo

Các vấn đề liên quan