Eu tenho duas tabelas, a primeira tabela contém todos os artigos / postagens de blog em um CMS. Alguns desses artigos também podem aparecer em uma revista; nesse caso, eles têm um relacionamento de chave estrangeira com outra tabela que contém informações específicas da revista.
Aqui está uma versão simplificada da sintaxe de criação de tabela para essas duas tabelas com algumas linhas não essenciais removidas:
CREATE TABLE `base_article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_published` datetime DEFAULT NULL,
`title` varchar(255) NOT NULL,
`description` text,
`content` longtext,
`is_published` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `base_article_date_published` (`date_published`),
KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `mag_article` (
`basearticle_ptr_id` int(11) NOT NULL,
`issue_slug` varchar(8) DEFAULT NULL,
`rubric` varchar(75) DEFAULT NULL,
PRIMARY KEY (`basearticle_ptr_id`),
KEY `mag_article_issue_slug` (`issue_slug`),
CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
O CMS contém cerca de 250.000 artigos no total e eu escrevi um script Python simples que pode ser usado para preencher um banco de dados de teste com dados de amostra, se eles quiserem replicar esse problema localmente.
Se eu selecionar uma dessas tabelas, o MySQL não terá problemas para escolher um índice apropriado ou recuperar artigos rapidamente. No entanto, quando as duas tabelas são unidas em uma consulta simples, como:
SELECT * FROM `base_article`
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30
O MySQL falha ao escolher uma consulta e desempenho apropriados. Aqui está a explicação relevante estendida (cujo tempo de execução é superior a um segundo):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 1 | SIMPLE | mag_article | ALL | PRIMARY | NULL | NULL | NULL | 23830 | 100.00 | Using temporary; Using filesort |
| 1 | SIMPLE | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4 | my_test.mag_article.basearticle_ptr_id | 1 | 100.00 | Using where |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- EDITAR SETEMBRO 30: Posso remover a
WHERE
cláusula desta consulta, mas aEXPLAIN
aparência ainda é a mesma e a consulta ainda é lenta.
Uma solução potencial é forçar um índice. A execução da mesma consulta com FORCE INDEX (base_articel_date_published)
resultados em uma consulta que é executada em cerca de 1,6 milissegundos.
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 1 | SIMPLE | base_article | index | NULL | base_article_date_published | 9 | NULL | 30 | 833396.69 | Using where |
| 1 | SIMPLE | mag_article | eq_ref | PRIMARY | PRIMARY | 4 | my_test.base_article.id | 1 | 100.00 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Eu preferiria não precisar forçar um índice nessa consulta, se eu puder evitá-la, por vários motivos. O mais notável é que essa consulta básica pode ser filtrada / modificada de várias maneiras (como a filtragem por issue_slug
), após as quais base_article_date_published
pode não ser mais o melhor índice a ser usado.
Alguém pode sugerir uma estratégia para melhorar o desempenho desta consulta?
base_article_is_published
(is_published
) .. olha para mim, é um tipo de boolean ..Respostas:
Que tal isso deve remover a necessidade de um "Uso temporário; Usando arquivosort" porque os dados já estão na classificação correta.
Você precisa saber o truque porque o MySQL precisa "Usando temporário; Usando filesort" para remover essa necessidade.
Consulte o segundo sqlfriddle para obter uma explicação sobre como remover a necessidade
consulte http://sqlfiddle.com/#!2/302710/2
Funciona muito bem. Eu também precisava disso há algum tempo para tabelas de país / cidade. Veja a demonstração aqui com dados de exemplo http://sqlfiddle.com/#!2/b34870/41
Editado, você também pode querer analisar esta resposta se base_article.is_published = 1 sempre retornar 1 registro, como sua explicação explicou, uma tabela de entrega interna INNER JOIN pode oferecer melhor desempenho, como as consultas na resposta abaixo
/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937
fonte
JOIN
apenas mas o MySQL não estava pegando o índice. Muito obrigado RaymondREFator A Consulta
ou
MODIFIQUE SEUS ÍNDICES
DE UMA CHANCE !!!
fonte
LIMIT 30
está na subconsulta (nem todas as 30 linhas também estarão namag_articles
tabela). Se eu mover aLIMIT
consulta externa, o desempenho será o mesmo do original. Modificar índices: O MySQL também não usa esse índice. Remover aWHERE
cláusula da minha consulta original não parece fazer diferença.