2013-07-19 7 views
8

Tôi đang cố gắng tìm ra lý do tại sao truy vấn của tôi chậm lại và cách tôi có thể khắc phục nhưng tôi hơi bối rối về kết quả của mình.Tại sao MySQL chậm khi sử dụng LIMIT trong truy vấn của tôi?

Tôi có một bảng orders với khoảng 80 cột và 775.179 hàng và tôi đang làm theo yêu cầu sau:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200

trả về 38 dòng trong 4.5s

Khi tháo ORDER BY tôi 'm nhận được một cải tiến tốt đẹp:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL LIMIT 200

38 hàng trong 0.30s

Nhưng khi tháo LIMIT mà không cần chạm vào ORDER BY tôi nhận được một kết quả thậm chí tốt hơn:

SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC

38 hàng trong 0.10s (??)

Tại sao LIMIT của tôi lại quá đói?

ĐI THÊM

Tôi đã cố gắng một vài điều trước khi gửi câu trả lời của tôi và sau khi nhận thấy rằng tôi đã có một chỉ mục trên creation_date (mà là một datetime) Tôi đã gỡ bỏ nó và truy vấn đầu tiên bây giờ chạy trong 0.10s . Tại sao vậy ?

EDIT

Tốt đoán, tôi có chỉ số trên các cột khác là một phần của nơi.

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC LIMIT 200; 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra  | 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 
| 1 | SIMPLE  | orders | index | id_state_idx,id_mp_idx | creation_date | 5  | NULL | 1719 | Using where | 
+----+-------------+--------+-------+------------------------+---------------+---------+------+------+-------------+ 

1 dòng trong set (0.00 sec)

mysql> explain SELECT * FROM orders WHERE id_state = 2 AND id_mp IS NOT NULL ORDER BY creation_date DESC; 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
| id | select_type | table | type | possible_keys   | key  | key_len | ref | rows | Extra            | 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
| 1 | SIMPLE  | orders | range | id_state_idx,id_mp_idx | id_mp_idx | 3  | NULL | 87502 | Using index condition; Using where; Using filesort | 
+----+-------------+--------+-------+------------------------+-----------+---------+------+-------+----------------------------------------------------+ 
+5

Vì sau truy vấn đầu tiên, kết quả được (một phần) được lưu trong bộ nhớ truy vấn sau ... – Mr47

+1

Đây không phải là trường hợp, chạy lại các truy vấn giống nhau đang cho tôi kết quả nhất quán –

+0

Có thể máy chủ của bạn bị chậm .... – pattyd

Trả lời

6

chỉ số không nhất thiết là cải thiện hiệu suất. Để hiểu rõ hơn về những gì đang xảy ra, nó sẽ giúp ích nếu bạn đưa vào số explain cho các truy vấn khác nhau.

Dự đoán tốt nhất của tôi là bạn có chỉ mục trong id_state hoặc thậm chí id_state, id_mp có thể được sử dụng để đáp ứng mệnh đề where. Nếu có, truy vấn đầu tiên không có order by sẽ sử dụng chỉ mục này. Nó sẽ khá nhanh. Ngay cả khi không có chỉ mục, điều này yêu cầu quét tuần tự các trang trong bảng orders, điều này vẫn có thể khá nhanh.

Sau đó, khi bạn thêm chỉ mục vào creation_date, MySQL sẽ quyết định sử dụng chỉ mục đó thay cho order by. Điều này đòi hỏi phải đọc từng hàng trong chỉ mục, sau đó tìm nạp trang dữ liệu tương ứng để kiểm tra các điều kiện where và trả về các cột (nếu có một kết quả phù hợp). Việc đọc này rất kém hiệu quả, bởi vì nó không nằm trong thứ tự "trang" mà đúng hơn là được chỉ định bởi chỉ mục. Các lần đọc ngẫu nhiên có thể khá không hiệu quả.

Tệ hơn nữa, mặc dù bạn có limit, bạn vẫn phải đọc toàn bộ bảng toàn bộ vì toàn bộ bộ kết quả là cần thiết. Mặc dù bạn đã lưu một loại trên 38 bản ghi, bạn đã tạo ra một truy vấn ồ ạt không hiệu quả.

Nhân tiện, tình trạng này trở nên tồi tệ hơn đáng kể nếu bảng orders không vừa với bộ nhớ khả dụng. Sau đó, bạn có một điều kiện được gọi là "đập", trong đó mỗi bản ghi mới có xu hướng tạo ra một I/O mới đọc. Vì vậy, nếu một trang có 100 bản ghi trên đó, trang có thể phải được đọc 100 lần.

Bạn có thể làm cho tất cả các truy vấn này chạy nhanh hơn bằng cách có chỉ mục trên orders(id_state, id_mp, creation_date). Mệnh đề where sẽ sử dụng hai cột đầu tiên và order by sẽ sử dụng điều cuối cùng.

+0

Câu trả lời tuyệt vời, nhưng trên một bảng của 775179 hồ sơ tôi sẽ nói chỉ mục là cần thiết phải không? –

+1

@KayNelson. . . Vâng. Đó là lý do tại sao tôi đề xuất một chỉ số tốt hơn trong đoạn cuối cùng. Tôi muốn được một chút cẩn thận, bởi vì không có 'giải thích' câu trả lời là tất cả đầu cơ. –

+0

Vâng, điều đó rất đúng! –

0

Cùng một vấn đề xảy ra trong dự án của tôi, tôi đã làm một số xét nghiệm, và phát hiện ra LIMIT đó là chậm vì tra cứu hàng

Xem: MySQL ORDER BY/LIMIT performance: late row lookups

Vì vậy, giải pháp là:

(A) khi sử dụng LIMIT, không chọn tất cả các cột, nhưng chỉ có các cột PK

(B) Chọn tất cả các cột bạn cần và sau đó kết hợp với tập hợp kết quả (A)

SQL nên thích:

SELECT 
    * 
FROM 
    orders O1 <=== this is what you want 
JOIN 
    (
     SELECT 
      ID       <== fetch the PK column only, this should be fast 
     FROM 
      orders 
     WHERE 
      [your query condition]  <== filter record by condition 
     ORDER BY 
      [your order by condition] <== control the record order 
     LIMIT 2000, 50     <== filter record by paging condition 
    ) as O2 
ON 
    O1.ID = O2.ID 
ORDER BY 
    [your order by condition]   <== control the record order 

trong DB của tôi,

SQL cũ mà chọn tất cả các cột sử dụng "LIMIT 21.560, 20", giá khoảng 4.484s.

chi phí sql mới chỉ có 0,063 giây. Số mới nhanh hơn khoảng 71 lần