Чи покращує використання 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.