Cenário resumido: uma tabela com mais de 16 milhões de registros [2 GB de tamanho]. Quanto maior o deslocamento de LIMIT com SELECT, mais lenta a consulta se torna, ao usar ORDER BY * primary_key *
assim
SELECT * FROM large ORDER BY `id` LIMIT 0, 30
leva muito menos do que
SELECT * FROM large ORDER BY `id` LIMIT 10000, 30
Isso só encomenda 30 registros e mesmo assim. Portanto, não é a sobrecarga de ORDER BY.
Agora, ao buscar as últimas 30 linhas, leva cerca de 180 segundos. Como posso otimizar essa consulta simples?
mysql
performance
sql-order-by
limit
Rahman
fonte
fonte
Respostas:
É normal que compensações mais altas atrasem a consulta, pois a consulta precisa contar os primeiros
OFFSET + LIMIT
registros (e tirar apenasLIMIT
deles). Quanto maior esse valor, mais a consulta é executada.A consulta não pode ir diretamente para
OFFSET
, porque, primeiro, os registros podem ter comprimentos diferentes e, segundo, pode haver falhas nos registros excluídos. Ele precisa verificar e contar cada registro a caminho.Assumindo que
id
é umPRIMARY KEY
de umaMyISAM
tabela, você pode acelerá-lo usando este truque:Veja este artigo:
fonte
ORDER BY
ou o índice cobrir todos os campos necessários, você não precisará dessa solução alternativa.postgresql
. Esta é uma resposta específica do MySQL.Eu mesmo tive o mesmo problema. Como você deseja coletar uma grande quantidade desses dados e não um conjunto específico de 30, provavelmente você estará executando um loop e incrementando o deslocamento em 30.
Então, o que você pode fazer é:
WHERE id > lastId limit 0,30
Assim, você sempre pode ter um deslocamento ZERO. Você ficará surpreso com a melhoria de desempenho.
fonte
O MySQL não pode ir diretamente para o 10000º registro (ou o 80000º byte como sugerido) porque ele não pode assumir que ele foi compactado / ordenado dessa maneira (ou que possui valores contínuos em 1 a 10000). Embora possa ser assim na realidade, o MySQL não pode assumir que não há buracos / lacunas / IDs excluídos.
Portanto, como observou o bobs, o MySQL precisará buscar 10000 linhas (ou percorrer as 10000ª entradas do índice
id
) antes de encontrar as 30 para retornar.EDIT : Para ilustrar o meu ponto
Observe que, embora
seria lento (er) ,
seria rápido (er) e retornaria os mesmos resultados, desde que não houvesse
id
s ausentes (ou seja, lacunas).fonte
Encontrei um exemplo interessante para otimizar consultas SELECT ORDER BY id LIMIT X, Y. Eu tenho 35 milhões de linhas, então demorei 2 minutos para encontrar um intervalo de linhas.
Aqui está o truque:
Basta colocar WHERE com o último id que você conseguiu aumentar muito o desempenho. Para mim, foi de 2 minutos a 1 segundo :)
Outros truques interessantes aqui: http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/
Também funciona com strings
fonte
A parte demorada das duas consultas é recuperar as linhas da tabela. Logicamente falando, na
LIMIT 0, 30
versão, apenas 30 linhas precisam ser recuperadas. NaLIMIT 10000, 30
versão, 10000 linhas são avaliadas e 30 linhas são retornadas. Pode haver alguma otimização no processo de leitura de dados, mas considere o seguinte:E se você tivesse uma cláusula WHERE nas consultas? O mecanismo deve retornar todas as linhas qualificadas e, em seguida, classificar os dados e, finalmente, obter as 30 linhas.
Considere também o caso em que as linhas não são processadas na sequência ORDER BY. Todas as linhas qualificadas devem ser classificadas para determinar quais linhas retornar.
fonte
Para aqueles que estão interessados em uma comparação e figuras :)
Experiência 1: o conjunto de dados contém cerca de 100 milhões de linhas. Cada linha contém vários campos BIGINT, TINYINT, bem como dois campos de texto (deliberadamente) contendo cerca de 1k caracteres.
SELECT * FROM post ORDER BY id LIMIT {offset}, 5
SELECT t.* FROM (SELECT id FROM post ORDER BY id LIMIT {offset}, 5) AS q JOIN post t ON t.id = q.id
... WHERE id>xxx LIMIT 0,5
,, não aparece aqui, pois deve ser tempo constante.Experiência 2: Coisa semelhante, exceto que uma linha possui apenas 3 BIGINTs.
fonte