L'uso di LIMIT migliora le prestazioni ed è evidente?


11

Voglio capire quanto segue.
Supponiamo che io abbia una query complicata con diciamo un join di 5 tabelle un gruppo per somme e ordina per.
Lasciare da parte eventuali ottimizzazioni alla query stessa, ad esempio indici ecc.
Vi sono vantaggi significativi in ​​termini di prestazioni LIMIT? Presumo che tutta la query (e i risultati) debbano essere elaborati prima dell'applicazione di LIMIT, quindi l'utilizzo di un LIMIT per recuperare un sottoinsieme dei risultati, offre qualche miglioramento significativo / evidente?


2
Ti suggerisco di leggere questo, per i casi che LIMITmigliorano l'efficienza: Ottimizzare le query LIMIT
ypercubeᵀᴹ

Risposte:


10

Se si desidera trarre vantaggio dal LIMITmiglioramento delle prestazioni, è necessario

  • capire i dati che stai recuperando
  • indicizzazione corretta della sequenza corretta di colonne
  • assumersi la responsabilità del refactoring della query
  • usando LIMITprimaJOIN

Questi principi possono fare molto se puoi orchestrarli.

Ho imparato questi concetti guardando questo video di YouTube (ascolta attentamente l'accento francese)

Ho usato questi concetti per rispondere a una domanda StackOverflow molto difficile su come ottenere i primi 40 articoli da alcune tabelle: 12 maggio 2011: recupero di una singola riga dalla tabella di join .

Nella mia risposta a quella domanda (16 maggio 2011) , ho scritto la seguente query e l'ho testata a fondo:

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;

Si prega di notare la riga nella query con il LIMIT

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

Questa sottoquery è sepolta a tre livelli di profondità. Questo mi ha permesso di ottenere gli ultimi 40 articoli usando LIMIT. Successivamente, ho eseguito i JOIN necessari in seguito.

LEZIONI IMPARATE

  • Fare una LIMITsubquery interna potrebbe non essere sempre la risposta a causa della cardinalità degli indici, del contenuto dei dati e della dimensione del set di risultati dal LIMIT. Se hai tutte le tue "anatre di fila" (hai in mente i quattro principi per la tua domanda), puoi ottenere risultati sorprendentemente buoni.
  • Rendi le tue domande il più semplice possibile quando lo fai LIMITraccogliendo solo le chiavi.

Quindi (A [LEFT] JOIN B) LIMIT 100è equivalente a (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Dove [LEFT] JOINsignifica join esterno o interno
Jim,

È più simile (A LIMIT 100) [LEFT] JOIN B. L'idea è quella di utilizzare LIMITper determinare la dimensione del set di risultati il ​​più presto possibile. Uso anche al LEFT JOINposto di INNER JOINperché LEFT JOINconserverò l'ordine dei tasti sul lato sinistro.
RolandoMySQLDBA il

@Jim No, non lo sono. A volte, sono come questo: di (A LEFT JOIN B) GROUP BY A.pk LIMIT 100solito possono essere riscritti come (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(nessun JOIN INNER qui, con join interni non sarebbero equivalenti). L'esempio di Rolando è esattamente un caso del genere.
ypercubeᵀᴹ

@ypercube: Quindi con i join interni non c'è qualcosa da fare per beneficiare di LIMIT?
Jim,

Mi riferivo alla strategia di riscrittura delineata da Rolando. Anche una query con JOIN e LIMIT può trarne vantaggio. O no. Dipende.
ypercubeᵀᴹ

2

Quando viene eseguita una query, viene prima tradotta in un piano composto da più operatori. Esistono due tipi base di operatori: blocco e non blocco. Un operatore non bloccante recupera una riga (o alcune righe) dal proprio figlio o dai propri figli per ogni riga richiesta da esso. Un operatore bloccante deve invece leggere ed elaborare l'intero set di righe di tutti i suoi figli prima che possa produrre qualsiasi output.

L'ordinamento è un tipico operatore di blocco. Quindi una selezione con ordine non beneficia molto di un limite. Tuttavia, esistono RDBMS che possono utilizzare un algoritmo di ordinamento che richiede meno memoria ed è più veloce quando viene fornita una clausola limite. In questo caso è sufficiente memorizzare le prime n righe attualmente presenti e spostarle dalla memoria man mano che arrivano le righe precedenti. Questo può essere un significativo miglioramento delle prestazioni. Tuttavia, non sono sicuro al 100% che MySQL abbia questa capacità.

In entrambi i casi, anche un ordinamento limite deve comunque elaborare l'intero set di righe di input prima che possa produrre la prima riga di output. Mentre questo algoritmo, se implementato, può velocizzare l'ordinamento, se il resto della query è la parte più costosa, il tempo di esecuzione totale non migliorerà significativamente a causa di un limite fornito.


Sono un po 'confuso con la risposta. Hai parlato di ordinamento ma anche di raggruppamento per specie, no? Quindi, se per esempio ho rimosso l'ordine di e mi attengo al gruppo di, la tua risposta si applica ancora? O è necessaria un'analisi diversa?
Jim,

A seconda della query e degli indici presenti, GROUP BYpotrebbe potenzialmente portare a un piano che non contiene operatori di blocco.
Sebastian Meine,

0

Nel mio caso, posso dire di , anche se (ancora) non capisco il perché.

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

Nota il tempo: 18 secondi. Stessa richiesta con un grande LIMIT:

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

Più di dieci volte più veloce !!!

EXPLAIN fornisce lo stesso risultato per entrambe le richieste.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 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 dovrebbe interferire solo per limitare il set di risultati (cioè, se faccio un LIMIT 4, ho solo le prime 4 righe del set di risultati sopra).


terrificante, quale versione stai usando e puoi creare un test case semplificato?
Evan Carroll,

1
La tua risposta non dimostra alcun nuovo vantaggio per LIMIT. La tua prima query viene eseguita in 18 secondi fornendo un set di risultati. Tutti i dati nella seconda query sono già memorizzati nella cache del pool di buffer InnoDB a causa della prima query, quindi ovviamente la seconda query deve essere più veloce, anche se riavvii mysql, esegui la prima query, riavvia mysql ed esegui la seconda query, otterrai lo stesso risultato. . Avere un risultato migliore per LIMITpuò venire solo dal fare: 1) LIMITprima JOIN, 2) LIMIT nell'ordine ASCo DESC.
RolandoMySQLDBA

Grazie per l'interesse. Creare un caso di test semplificato potrebbe essere difficile.
Pierre-Olivier Vares,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.