Mam dwie tabele, pierwsza tabela zawiera wszystkie artykuły / posty na blogu w systemie CMS. Niektóre z tych artykułów mogą również pojawiać się w czasopiśmie, w którym to przypadku mają związek z kluczem obcym z inną tabelą zawierającą informacje specyficzne dla czasopisma.
Oto uproszczona wersja składni tworzenia tabeli dla tych dwóch tabel z usuniętymi nieistotnymi wierszami:
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;
CMS zawiera łącznie około 250 000 artykułów, a ja napisałem prosty skrypt w języku Python, którego można użyć do wypełnienia testowej bazy danych przykładowymi danymi, jeśli chcą replikować ten problem lokalnie.
Jeśli wybiorę jedną z tych tabel, MySQL nie będzie miał problemu z wybraniem odpowiedniego indeksu lub szybkim pobieraniem artykułów. Jednak gdy dwie tabele są ze sobą połączone w prostym zapytaniu, takim jak:
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 nie wybiera odpowiedniego zapytania i poprawia wydajność. Oto odpowiednie wyjaśnienie wydłużone (czas wykonania wynosi ponad sekundę):
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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 |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
- EDYCJA WRZESIEŃ 30: Mogę usunąć
WHERE
klauzulę z tego zapytania, aleEXPLAIN
nadal wygląda tak samo, a zapytanie jest nadal wolne.
Jednym z potencjalnych rozwiązań jest wymuszenie indeksu. Uruchomienie tego samego zapytania FORCE INDEX (base_articel_date_published)
powoduje, że zapytanie wykonuje się w około 1,6 milisekundy.
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 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 | |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
Wolałbym nie zmuszać indeksu do tego zapytania, jeśli mogę tego uniknąć z kilku powodów. Co najważniejsze, to podstawowe zapytanie może być filtrowane / modyfikowane na różne sposoby (takie jak filtrowanie według issue_slug
), po czym base_article_date_published
może nie być najlepszym indeksem do użycia.
Czy ktoś może zasugerować strategię poprawy wydajności dla tego zapytania?
base_article_is_published
(is_published
) .. wygląda na to, że jest to typ boolowski.Odpowiedzi:
Co z tego, powinno to wyeliminować potrzebę „Używania tymczasowego; Używania sortowania plików”, ponieważ dane są już w odpowiednim sortowaniu.
Musisz wiedzieć, dlaczego MySQL potrzebuje „Używanie tymczasowego; Używanie sortowania plików”, aby usunąć tę potrzebę.
Zobacz drugi sqlfriddle, aby uzyskać wyjaśnienie na temat usuwania potrzeby
patrz http://sqlfiddle.com/#!2/302710/2
Działa całkiem dobrze, potrzebowałem tego również jakiś czas temu dla tabel kraju / miasta zobacz demo tutaj z przykładowymi danymi http://sqlfiddle.com/#!2/b34870/41
Edytowany możesz również przeanalizować tę odpowiedź, jeśli base_article.is_published = 1 zawsze zwraca 1 rekord, tak jak wyjaśniono wyjaśnienie, tabela dostarczona z WEJŚCIEM WEWNĘTRZNYM może dać lepszą wydajność, podobnie jak zapytania w odpowiedzi poniżej
/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937
źródło
JOIN
tylko, ale MySQL nie pobierał indeksu. Dzięki bardzo RaymondREFAKTOR ZAPYTANIA
lub
ZMODYFIKUJ SWOJE INDEKSY
SPRÓBUJ !!!
źródło
LIMIT 30
jest w podzapytaniu (nie wszystkie z tych 30 wierszy również będą wmag_articles
tabeli). Jeśli przejdęLIMIT
do zewnętrznego zapytania, wydajność będzie taka sama jak w moim oryginale. Modyfikuj indeksy: MySQL również nie używa tego indeksu. UsunięcieWHERE
klauzuli z mojego pierwotnego zapytania nie wydaje się mieć znaczenia.