Ho due tabelle, la prima tabella contiene tutti gli articoli / post di blog in un CMS. Alcuni di questi articoli possono anche apparire in una rivista, nel qual caso hanno una relazione di chiave esterna con un'altra tabella che contiene informazioni specifiche della rivista.
Ecco una versione semplificata della sintassi della tabella di creazione per queste due tabelle con alcune righe non essenziali eliminate:
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;
Il CMS contiene circa 250.000 articoli in totale e ho scritto un semplice script Python che può essere utilizzato per popolare un database di test con dati di esempio se vogliono replicare questo problema localmente.
Se seleziono da una di queste tabelle, MySQL non ha problemi a scegliere un indice appropriato o a recuperare rapidamente gli articoli. Tuttavia, quando le due tabelle vengono unite insieme in una semplice query come:
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
MySQL non riesce a scegliere una query appropriata e il crollo delle prestazioni. Ecco la spiegazione pertinente estesa (il tempo di esecuzione è superiore a un secondo):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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 |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- MODIFICA 30 SETTEMBRE: posso rimuovere la
WHERE
clausola da questa query, maEXPLAIN
sembra sempre la stessa e la query è ancora lenta.
Una potenziale soluzione è forzare un indice. L'esecuzione della stessa query con FORCE INDEX (base_articel_date_published)
risultati in una query che viene eseguita in circa 1,6 millisecondi.
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 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 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Preferirei non dover forzare un indice su questa query se posso evitarlo, per diversi motivi. In particolare, questa query di base può essere filtrata / modificata in vari modi (come il filtro per issue_slug
), dopodiché base_article_date_published
potrebbe non essere più il miglior indice da utilizzare.
Qualcuno può suggerire una strategia per migliorare le prestazioni di questa query?
base_article_is_published
(is_published
) .. mi sembra che sia un tipo booleano ..