Чи покращує використання LIMIT продуктивність і чи це помітно?


11

Я хочу зрозуміти наступне.
Припустимо, що у мене складний запит, скажімо, об'єднання 5 таблиць у групу за підсумками та упорядкуванням по.
Відкидаючи будь-які оптимізації самого запиту, наприклад, індекси тощо.
Чи є якась значна користь від ефективності використання LIMIT? Я припускаю, що всі запити (та результати) повинні бути оброблені до того, як буде застосовано LIMIT, тому, використовуючи LIMIT для отримання підмножини результатів, чи пропонує це якесь суттєве / помітне покращення?


2
Я пропоную вам прочитати це для випадків, які LIMITпідвищують ефективність: Оптимізація LIMIT Queries
— ypercubeᵀᴹ

Відповіді:


10

Якщо ви хочете скористатися LIMITдля покращення продуктивності, вам потрібно

  • зрозуміти дані, які ви отримуєте
  • належна індексація правильної послідовності стовпців
  • взяти на себе відповідальність за рефакторинг запиту
  • використання LIMITранішеJOIN

Ці принципи можуть пройти довгий шлях, якщо ви можете їх оркеструвати.

Я дізнався ці концепції, переглянувши це відео YouTube (уважно слухайте французький акцент)

Я використовував ці поняття, щоб відповісти на дуже складний питання StackOverflow про отримання 40 найкращих статей з деяких таблиць: 12 травня 2011 року: Вилучення єдиного рядка з таблиці приєднання .

У своїй відповіді на це запитання (16 травня 2011 р.) Я написав наступний запит і ретельно перевірив його:

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;

Зауважте, рядок у запиті з LIMIT

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

Цей підзапит похований у глибині трьох рівнів. Це дозволило мені отримати останні 40 статей за допомогою LIMIT. Потім я виконав необхідні ПРИЄДНАННЯ після цього.

НАВЧАННЯ УРОКІВ

  • Здійснення LIMITвсередині підзапитів не завжди може бути відповіддю через простоту індексів, вміст даних та розмір набору результатів від LIMIT. Якщо у вас є всі "качки підряд" (майте на увазі чотири принципи для свого запиту), ви можете отримати дивно хороші результати.
  • Зробіть запити максимально спрощеними, коли це робити LIMIT, збираючи лише ключі.

Так що (A [LEFT] JOIN B) LIMIT 100еквівалентно (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Де [LEFT] JOINозначає зовнішнє або внутрішнє приєднання
— Джим

Це більше схоже (A LIMIT 100) [LEFT] JOIN B. Ідея полягає у використанні LIMITдля визначення розміру набору результатів якомога раніше. Я також використовую LEFT JOINзамість того, INNER JOINщо LEFT JOINзбережу порядок клавіш ліворуч.
— RolandoMySQLDBA

@Jim Ні, вони не є. Іноді вони є, як цей: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100зазвичай можуть бути переписані як (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(жодного внутрішнього приєднання тут, якщо внутрішні з'єднання не були б еквівалентними.) Приклад Роландо - саме такий випадок.
— ypercubeᵀᴹ

@ypercube: Тож із внутрішнім приєднанням, чи не можна було б скористатися LIMIT?
— Джим

Я мав на увазі стратегію переписування, окреслену Роландо. Запит з JOINs та LIMIT може також виграти. Чи ні. Це залежить.
— ypercubeᵀᴹ

2

Коли запит виконується, він спочатку переводиться в план, який складається з декількох операторів. Існує два основних типи операторів: Блокування та Неблокування. Оператор, що не блокує, отримує рядок (або кілька рядків) від своєї дитини або дітей для кожного запитуваного від нього рядка. З іншого боку, оператор блокування повинен прочитати і обробити весь набір рядків усіх своїх дітей, перш ніж він зможе отримати будь-який вихід.

Сортування - типовий оператор блокування. Таким чином, вибір із замовленням не дуже корисний від ліміту. Однак є RDBMS, які можуть використовувати алгоритм сортування, який потребує меншої пам’яті та швидший, коли передбачено обмеження. У цьому випадку достатньо лише зберегти перші n n рядків і перемістити їх із пам'яті, коли збираються попередні рядки. Це може бути значним збільшенням продуктивності. Однак я не на 100% впевнений, що MySQL має таку здатність.

У будь-якому випадку навіть лінійному сортуванню все-таки потрібно обробити весь набір вхідних рядків, перш ніж він може створити перший вихідний рядок. Хоча цей алгоритм, якщо він буде реалізований, може прискорити сортування, якщо решта запиту є найдорожчою частиною, загальний час виконання значно не покращиться через передбачений ліміт.


Я трохи не плутаю відповідь. Ви згадуєте про сортування, але група також сортує, чи не так? Тож якщо я, наприклад, видалив замовлення і дотримуюся групи, чи відповідає ваша відповідь? Або потрібен інший аналіз?
— Джим

Залежно від запиту та наявних індексів, GROUP BYце потенційно може призвести до плану, який не містить операторів, що блокують.
— Себастьян Майне

0

У моєму випадку я можу сказати Так , навіть якщо я (досі) не розумію, чому.

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**)

Зверніть увагу на час: 18 секунд. Той самий запит з великим ГРОМОМ:

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**)

Більш ніж у десять разів швидше !!!

ПОЯСНЕННЯ дають однаковий результат для обох запитів.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 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                                  |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+

LIMIT повинен заважати лише обмеженню набору результатів (тобто, якщо я зробив LIMIT 4, я отримав лише перші 4 ряди вищевказаного набору результатів).


жахливо, яку версію ви використовуєте і чи можете ви створити спрощений тестовий випадок?
— Еван Керролл

1
Ваша відповідь не підтверджує жодної нової користі LIMIT. Ваш перший запит запускається за 18 секунд, надаючи набір результатів. Всі дані у 2-му запиті вже кешовані в пулі InnoDB завдяки першому запиту. Отже, звичайно, 2-й запит повинен бути швидшим. Навіть якщо ви перезапустите mysql, запустіть 1-й запит, перезапустіть mysql та запустіть 2-й. запит, ви отримаєте такий же результат. . Поліпшити результат LIMITможна лише за рахунок: 1) LIMITраніше JOIN, 2) ОБМЕЖЕННЯ в порядку сортування ASCабо DESC.
— RolandoMySQLDBA

Дякуємо за ваш інтерес. Створення спрощеного тестового випадку може бути складним.
— П’єр-Олів'є Варес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.