Việc sử dụng LIMIT có cải thiện hiệu suất và nó có đáng chú ý không?


11

Tôi muốn hiểu những điều sau đây.
Giả sử rằng tôi có một truy vấn phức tạp với giả sử tham gia 5 bảng một nhóm theo tổng và sắp xếp theo thứ tự.
Để bất kỳ tối ưu hóa nào cho chính truy vấn, ví dụ như các chỉ mục, vv
Có bất kỳ lợi ích hiệu suất đáng kể nào khi sử dụng LIMITkhông? Tôi giả sử rằng tất cả các truy vấn (và kết quả) phải được xử lý trước khi LIMIT được áp dụng, vì vậy sử dụng LIMIT để truy xuất tập hợp con của kết quả, điều này có mang lại sự cải thiện đáng kể / đáng chú ý nào không?


2
Tôi đề nghị bạn đọc điều này, đối với các trường hợp LIMITgiúp cải thiện hiệu quả: Tối ưu hóa các câu hỏi
GIỚI

Câu trả lời:


10

Nếu bạn muốn tận dụng LIMITđể cải thiện hiệu suất, bạn cần

  • hiểu dữ liệu bạn đang truy xuất
  • lập chỉ mục đúng các chuỗi chính xác
  • chịu trách nhiệm tái cấu trúc truy vấn
  • sử dụng LIMITtrướcJOIN

Những nguyên tắc này có thể đi một chặng đường dài nếu bạn có thể sắp xếp chúng.

Tôi đã học được những khái niệm này bằng cách xem Video YouTube này (lắng nghe cẩn thận qua giọng Pháp)

Tôi đã sử dụng các khái niệm đó để trả lời một câu hỏi StackOverflow rất khó khăn về việc nhận 40 bài viết hàng đầu từ một số bảng: ngày 12 tháng 5 năm 2011: Tìm nạp một hàng từ Bảng tham gia .

Trong câu trả lời của tôi cho câu hỏi đó (ngày 16 tháng 5 năm 2011) , tôi đã viết truy vấn sau đây và kiểm tra nó kỹ lưỡng:

SELECT
  AAA.author_id,
  AAA.date_created,
  IFNULL(BBB.title,'<NO_TITLE>') title,
  IFNULL(CCC.filename,'<NO-IMAGE>') filename,
  IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
  SELECT
    AA.id,
    AA.date_added,
    BB.author_id,
    BB.date_created
  FROM
  (
    SELECT
      A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
      LEFT JOIN article_images B ON A.id = B.article_id
      GROUP BY A.id
  ) AA
  INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;

Vui lòng chú ý dòng trong truy vấn với LIMIT

      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A

Truy vấn này được chôn sâu ba cấp độ. Điều này cho phép tôi có được 40 bài viết cuối cùng bằng cách sử dụng LIMIT. Sau đó, tôi đã thực hiện các THAM GIA cần thiết sau đó.

BÀI HỌC

  • Thực hiện LIMITbên trong các truy vấn con có thể không phải lúc nào cũng là câu trả lời vì tính chính xác của các chỉ mục, nội dung dữ liệu và kích thước tập kết quả từ LIMIT. Nếu bạn có tất cả "vịt liên tiếp" (Có bốn nguyên tắc trong truy vấn của bạn), bạn có thể nhận được kết quả tốt đáng ngạc nhiên.
  • Làm cho các truy vấn của bạn đơn giản nhất có thể khi thực hiện LIMITbằng cách chỉ thu thập các khóa.

Vậy (A [LEFT] JOIN B) LIMIT 100có tương đương với (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Trường hợp [LEFT] JOINcó nghĩa là tham gia bên ngoài hoặc bên trong
Jim

Nó giống như hơn (A LIMIT 100) [LEFT] JOIN B. Ý tưởng là sử dụng LIMITđể xác định kích thước của tập kết quả càng sớm càng tốt. Tôi cũng sử dụng LEFT JOINthay INNER JOINvì vì LEFT JOINsẽ giữ thứ tự các phím ở bên trái.
RolandoMySQLDBA

@Jim Không, họ không. Đôi khi, chúng giống như thế này: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100thường có thể được viết lại thành (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(không có INNER THAM GIA ở đây, với các phép nối bên trong chúng sẽ không tương đương.) Ví dụ của Rolando chính xác là một trường hợp như vậy.
ypercubeᵀᴹ

@ypercube: Vậy với những người tham gia bên trong không có gì để làm để hưởng lợi từ GIỚI HẠN?
Jim

Tôi đã đề cập đến chiến lược viết lại được Rolando vạch ra. Một truy vấn với THAM GIA và GIỚI HẠN cũng có thể có lợi. Hay không. Nó phụ thuộc.
ypercubeᵀᴹ

2

Khi một truy vấn được thực thi, đầu tiên nó sẽ được dịch thành một kế hoạch được tạo thành từ nhiều toán tử. Có hai loại toán tử cơ bản: Chặn và Không chặn. Toán tử không chặn lấy một hàng (hoặc một vài hàng) từ con hoặc con của nó cho mỗi hàng được yêu cầu từ nó. Mặt khác, một Toán tử chặn phải đọc và xử lý toàn bộ tập hợp hàng của tất cả các phần tử con của nó trước khi nó có thể tạo ra bất kỳ đầu ra nào.

Sắp xếp là một toán tử chặn điển hình. Vì vậy, một lựa chọn với thứ tự không có lợi nhiều từ một giới hạn. Tuy nhiên, có các RDBMS có thể sử dụng thuật toán sắp xếp cần ít bộ nhớ hơn và nhanh hơn khi cung cấp một mệnh đề giới hạn. Trong trường hợp này, chỉ cần lưu trữ n hàng đầu tiên hiện tại và di chuyển chúng ra khỏi bộ nhớ khi các hàng trước xuất hiện. Đó có thể là một hiệu suất đáng kể đạt được. Tuy nhiên, tôi không chắc chắn 100% rằng MySQL có khả năng đó.

Dù bằng cách nào, ngay cả một loại giới hạn vẫn cần xử lý toàn bộ hàng đầu vào được đặt trước khi nó có thể tạo ra hàng đầu ra đầu tiên. Mặc dù thuật toán này, nếu được triển khai, có thể tăng tốc độ sắp xếp, nếu phần còn lại của truy vấn là phần đắt nhất, tổng thời gian thực hiện sẽ không cải thiện đáng kể do giới hạn được cung cấp.


Tôi hơi bối rối với câu trả lời. Bạn đề cập đến sắp xếp nhưng nhóm cũng sắp xếp không? Vì vậy, nếu tôi đã loại bỏ thứ tự và gắn bó với nhóm, câu trả lời của bạn có còn được áp dụng không? Hoặc một phân tích khác nhau là cần thiết?
Jim

Tùy thuộc vào truy vấn và các chỉ mục hiện tại, GROUP BYcó khả năng có thể dẫn đến một kế hoạch không chứa các toán tử chặn.
Sebastian Meine

0

Trong trường hợp của tôi, tôi có thể nói , ngay cả khi tôi (vẫn) không hiểu tại sao.

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;

(result set)

8 rows in set (**18.14 sec**)

Lưu ý thời gian: 18 giây. Yêu cầu tương tự với GIỚI HẠN lớn:

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1 
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511 
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;

(exact same result set)

8 rows in set (**1.32 sec**)

Nhanh hơn gấp mười lần !!!

GIẢI THÍCH cho cùng một kết quả cho cả hai yêu cầu.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                                     | key     | key_len | ref                          | rows   | filtered | Extra                                        |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
|  1 | SIMPLE      | a1_   | NULL       | ALL    | IDX_438010BBC10784EF                              | NULL    | NULL    | NULL                         | 795135 |    33.33 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | g0_   | NULL       | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4       | phs.a1_.groupe_jardinerie_id |      1 |    50.00 | Using where                                  |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+

GIỚI HẠN chỉ can thiệp để giới hạn tập kết quả (nghĩa là, nếu tôi thực hiện GIỚI HẠN 4, tôi chỉ nhận được 4 hàng đầu tiên của tập kết quả ở trên).


thật đáng sợ, bạn đang sử dụng phiên bản nào và bạn có thể tạo một trường hợp thử nghiệm đơn giản hóa không?
Evan Carroll

1
Câu trả lời của bạn không chứng minh bất kỳ lợi ích mới cho LIMIT. Truy vấn đầu tiên của bạn chạy trong 18 giây cho một tập kết quả. Tất cả dữ liệu trong truy vấn thứ 2 đã được lưu trong bộ đệm InnoDB do truy vấn đầu tiên, do đó, tất nhiên truy vấn thứ 2 phải nhanh hơn, ngay cả khi bạn khởi động lại mysql, chạy truy vấn thứ nhất, khởi động lại mysql và chạy lần thứ 2 truy vấn, bạn sẽ nhận được kết quả tương tự. . Có kết quả tốt hơn LIMITchỉ có thể đến từ việc thực hiện: 1) LIMITtrước JOIN, 2) GIỚI HẠN theo thứ tự sắp xếp ASChoặc DESC.
RolandoMySQLDBA

Cảm ơn sự quan tâm của bạn. Tạo ra một trường hợp thử nghiệm đơn giản hóa có thể khó khăn.
Pierre-Olivier Vares
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.