O uso de LIMIT melhora o desempenho e é perceptível?

11

Eu quero entender o seguinte.
Suponha que eu tenha uma consulta complicada com, digamos, uma junção de 5 tabelas por grupo por somatórios e por ordem.
Deixando de lado quaisquer otimizações para a própria consulta, por exemplo, índices etc.
Existe algum benefício significativo de desempenho usando LIMIT? Suponho que toda a consulta (e resultados) deve ser processada antes da aplicação de LIMIT; portanto, usando um LIMIT para recuperar um subconjunto dos resultados, isso oferece alguma melhoria significativa / perceptível?

Jim
fonte
2
Eu sugiro que você leia isso, para os casos que LIMITmelhora a eficiência: Otimizando LIMIT Consultas
ypercubeᵀᴹ

Respostas:

10

Se você deseja aproveitar LIMITpara melhorar o desempenho, precisa

  • entender os dados que você está recuperando
  • indexação adequada da sequência correta de colunas
  • assumir a responsabilidade de refatorar a consulta
  • usando LIMITantesJOIN

Esses princípios podem percorrer um longo caminho se você puder orquestrá-los.

Aprendi esses conceitos assistindo a este vídeo do YouTube (ouça com atenção o sotaque francês)

Usei esses conceitos para responder a uma pergunta muito difícil do StackOverflow sobre como obter os 40 principais artigos de algumas tabelas: 12 de maio de 2011: Buscando uma única linha da tabela de junção .

Na minha resposta a essa pergunta (16 de maio de 2011) , escrevi a seguinte consulta e a testei completamente:

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;

Observe a linha na consulta com o LIMIT

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

Esta subconsulta está enterrada a três níveis de profundidade. Isso me permitiu obter os últimos 40 artigos usando LIMIT. Depois, executei os JOINs necessários depois.

LIÇÕES APRENDIDAS

  • Fazer LIMITsubconsultas internas nem sempre pode ser a resposta devido à cardinalidade dos índices, ao conteúdo dos dados e ao tamanho do conjunto de resultados do LIMIT. Se você tiver todos os seus "patos consecutivos" (tenha os quatro princípios em mente para sua consulta), poderá obter resultados surpreendentemente bons.
  • Faça suas consultas o mais simplista possível ao LIMITcoletar apenas as chaves.
RolandoMySQLDBA
fonte
Então (A [LEFT] JOIN B) LIMIT 100é equivalente a (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Onde [LEFT] JOINmeios exteriores ou interiores juntar
Jim
É mais como (A LIMIT 100) [LEFT] JOIN B. A idéia é usar LIMITpara determinar o tamanho do conjunto de resultados o mais cedo possível. Eu também uso em LEFT JOINvez de INNER JOINporque LEFT JOINpreservará a ordem das teclas no lado esquerdo.
RolandoMySQLDBA
@ Jim Não, eles não são. Às vezes, elas são, como esta: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100geralmente podem ser reescritas como (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(não há INNER JOIN aqui, com junções internas elas não seriam equivalentes.) O exemplo de Rolando é exatamente esse caso.
ypercubeᵀᴹ
@ypercube: Então, com junções internas, não há algo a fazer para se beneficiar do LIMIT?
23413 Jim
Eu estava me referindo à estratégia de reescrita descrita por Rolando. Uma consulta com JOINs e LIMIT também pode se beneficiar. Ou não. Depende.
ypercubeᵀᴹ
2

Quando uma consulta é executada, ela é traduzida primeiro em um plano composto por vários operadores. Existem dois tipos básicos de operadores: bloqueio e não bloqueio. Um Operador sem bloqueio recupera uma linha (ou algumas linhas) de seu filho ou filhos para cada linha solicitada a ele. Um Operador de Bloqueio, por outro lado, precisa ler e processar todo o conjunto de linhas de todos os seus filhos antes de poder produzir qualquer saída.

A classificação é um operador de bloqueio típico. Portanto, um select com order by não se beneficia muito de um limite. No entanto, existem RDBMSs que podem utilizar um algoritmo de classificação que precisa de menos memória e é mais rápido quando uma cláusula de limite é fornecida. Nesse caso, basta armazenar as primeiras n linhas atualmente e removê-las da memória à medida que as linhas anteriores aparecem. Isso pode ser um ganho significativo de desempenho. No entanto, não tenho 100% de certeza de que o MySQL tenha essa capacidade.

De qualquer forma, mesmo uma classificação de limite ainda precisa processar toda a linha de entrada definida antes de poder produzir a primeira linha de saída. Embora esse algoritmo, se implementado, possa acelerar a classificação, se o restante da consulta for a parte mais cara, o tempo total de execução não melhorará significativamente devido a um limite fornecido.

Sebastian Meine
fonte
Estou um pouco confuso com a resposta. Você menciona sobre classificação, mas agrupa também classifica, não é? Então, se, por exemplo, removi o pedido e permaneci no grupo por, sua resposta ainda se aplica? Ou é necessária uma análise diferente?
23413 Jim
Dependendo da consulta e dos índices presentes, isso GROUP BYpode levar a um plano que não contenha operadores de bloqueio.
Sebastian Meine
0

No meu caso, posso dizer sim , mesmo que eu (ainda) não entenda o porquê.

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

Observe o tempo: 18 segundos. Mesmo pedido com um 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**)

Mais de dez vezes mais rápido !!!

EXPLAIN fornece o mesmo resultado para os dois pedidos.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| 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 deve interferir apenas para limitar o conjunto de resultados (ou seja, se eu fizer um LIMIT 4, obtive apenas as 4 primeiras linhas do conjunto de resultados acima).

Pierre-Olivier Vares
fonte
aterrorizante, qual versão você está usando e pode criar um caso de teste simplificado?
Evan Carroll
11
Sua resposta não prova nenhum novo benefício para LIMIT. Sua primeira consulta é executada em 18 segundos, fornecendo um conjunto de resultados. Todos os dados da 2ª consulta já estão armazenados em cache no buffer pool do InnoDB devido à primeira consulta. Portanto, a 2ª consulta deve ser mais rápida. Mesmo se você reiniciar o mysql, execute a 1ª consulta, reinicie o mysql e execute a 2ª consulta. consulta, você obterá o mesmo resultado. . Ter um resultado melhor para LIMITsomente pode resultar de: 1) LIMITantes JOIN, 2) LIMIT na ordem de classificação ASCou DESC.
RolandoMySQLDBA 30/01
Obrigado pelo seu interesse. Criar um caso de teste simplificado pode ser difícil.
Pierre-Olivier Vares