2015-05-23 15 views
7

Czytałem inne posty, ale nie udało mi się poprawić mojego zapytania.Unikaj filesort z INNER JOIN + ZAMÓW PRZEZ

Jeśli zapytanie jest x20 razy wolniejsze, muszę to poprawić. Jest to zapytanie:

SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title 
FROM posts 
INNER JOIN follow ON posts.post_b_id = follow.board_id 
INNER JOIN boards ON posts.post_b_id = boards.board_id 
WHERE follow.user_id =1 
ORDER BY posts.post_id DESC 
LIMIT 10 

I to są tabele (Aktualizacja):

CREATE TABLE IF NOT EXISTS `posts` (
`post_id` int(11) NOT NULL AUTO_INCREMENT, 
`post_b_id` int(11) unsigned NOT NULL, 
`post_title` varchar(50) COLLATE utf8_bin NOT NULL, 
`post_cont` text COLLATE utf8_bin NOT NULL, 
`post_mintxt` varchar(255) COLLATE utf8_bin NOT NULL, 
`post_type` char(3) COLLATE utf8_bin NOT NULL, 
`thumb` varchar(200) COLLATE utf8_bin NOT NULL, 
`post_user` varchar(16) COLLATE utf8_bin NOT NULL, 
`published` enum('0','1') COLLATE utf8_bin NOT NULL, 
`post_ip` varchar(94) COLLATE utf8_bin NOT NULL, 
`post_ip_dat` int(11) unsigned NOT NULL, 
`post_up` int(10) unsigned NOT NULL DEFAULT '0', 
`post_down` int(10) unsigned NOT NULL DEFAULT '0', 
PRIMARY KEY (`post_id`), 
KEY `post_b_id` (`post_b_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=405 ; 

CREATE TABLE IF NOT EXISTS `boards` (
`board_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`board_title_l` varchar(19) COLLATE utf8_bin NOT NULL, 
`board_user_id` int(10) unsigned NOT NULL, 
`board_title` varchar(19) COLLATE utf8_bin NOT NULL, 
`board_user` varchar(16) COLLATE utf8_bin NOT NULL, 
`board_txt` tinyint(1) unsigned NOT NULL, 
`board_img` tinyint(1) unsigned NOT NULL, 
`board_vid` tinyint(1) unsigned NOT NULL, 
`board_desc` varchar(100) COLLATE utf8_bin NOT NULL, 
`board_mod_p` tinyint(3) unsigned NOT NULL DEFAULT '0', 
`board_ip` varchar(94) COLLATE utf8_bin NOT NULL, 
`board_dat_ip` int(11) unsigned NOT NULL, 
PRIMARY KEY (`board_id`), 
UNIQUE KEY `board_title_l` (`board_title_l`), 
KEY `board_user_id` (`board_user_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=89 ; 

CREATE TABLE IF NOT EXISTS `follow` (
`user_id` int(10) unsigned NOT NULL, 
`board_id` int(10) unsigned NOT NULL, 
PRIMARY KEY (`user_id`,`board_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 

Korzystanie domyślny ASC kolejności używa tylko indeksu i gdzie, z DESC wykorzystuje indeks, gdzie, tymczasowy i plik.

id select_type table type possible_keys key   key_len ref    rows filtered Extra 
1 SIMPLE  follow ref  user_id   user_id  4  const    2  100.00  Using index; Using temporary; Using filesort 
1 SIMPLE  boards eq_ref PRIMARY   PRIMARY  4 xxxx.follow.board_id 1  100.00 
1 SIMPLE  posts ref  post_b_id  post_b_id 4 xxxx.boards.board_id 3  100.00  Using where 

Jak mogę dokonać kwerendy otrzymaniu wyników w DESC zamówienie bez filesort i tymczasowe.

UPDATE: Zrobiłem nowe zapytanie, bez czasowego lub filesort, ale typu: indeks, sączy: 7340.00. Prawie tak szybko, jak ASC, jeśli posty znajdują się na końcu tabeli, ale są wolne, jeśli szukane posty znajdują się na początku. Wydaje się, że jest lepiej, ale to za mało.

SELECT posts.post_id, posts.post_b_id, posts.post_title, posts.post_cont, posts.thumb, posts.post_user, boards.board_title_l, boards.board_title 
FROM posts INNER JOIN boards ON posts.post_b_id = boards.board_id 
WHERE posts.post_b_id 
IN (
SELECT follow.board_id 
FROM follow 
WHERE follow.user_id = 1 
) 
ORDER BY posts.post_id DESC 
LIMIT 10 

Wyjaśnij:

id select_type  table type possible_keys key  key_len ref    rows filtered Extra 
1 PRIMARY   posts index post_b_id  PRIMARY  8  NULL    10  7340.00 Using where 
1 PRIMARY   boards eq_ref PRIMARY   PRIMARY  4 xxxx.posts.post_b_id 1  100.00 
2 DEPENDENT SUBQUERY follow eq_ref user_id   user_id  8  const,func   1  100.00  Using index 

UPDATE: Wyjaśnić dla zapytania od dened's answer:

id select_type table type possible_keys key  key_len ref    rows filtered Extra 
1 PRIMARY  <derived2>ALL NULL    NULL  NULL  NULL    10  100.00 
1 PRIMARY  posts eq_ref PRIMARY,post_b_id PRIMARY 4  sq.post_id  1  100.00  
1 PRIMARY  boards eq_ref PRIMARY   PRIMARY 4 xxxx.posts.post_b_id 1  100.00 
2 DERIVED  follow ref  PRIMARY   PRIMARY 4       1  100.00  Using index; Using temporary; Using filesort 
2 DERIVED  posts ref  post_b_id  post_b_id 4 xxxx.follow.board_id 6  100.00  Using index 

Times:

Original query no order (ASC):    0.187500 seconds 
Original query DESC:      2.812500 seconds 
Second query posts at the end (DESC):  0.218750 seconds 
Second query posts at the beginning (DESC): 3.293750 seconds 
dened's query DESC:       0.421875 seconds 
dened's query no order (ASC):    0.323750 seconds 

Interesująca wiadomość, jeśli dodaję ORDER BY ASC jest tak powolna jak DESC.

Zmieniaj kolejność stołów na boga, ale jak powiedziałem w komentarzach, nie byłem w stanie tego zrobić.

+1

To wygląda dobrze.Nie wiesz, co możesz zrobić :-( – Strawberry

+0

Możesz dodać indeks malejący na post_id – Martijn

+0

"InnoDB zawsze zamawia wiersze tabeli zgodnie z indeksem klastrowym." Próbowałem ALTER TABLE 'posts' ORDER BY' post_id' DESC i pozostało ASC w każdym razie, inne silniki mogą mieć zlecenie DESC, ale jeśli wstawisz lub usuniesz dowolny wiersz, to nie zachowujesz kolejności – Vixxs

Odpowiedz

1

Możesz pomóc MySQL optymalizator przesuwając całą pracę filtrowania do podkwerendzie co uzyskuje dostęp tylko indeksy (manipulowanie wskaźnikami jest zwykle znacznie szybciej niż manipulowanie inne dane), a ściągam resztę danych w zapytaniu peryferyjnymi:

SELECT posts.post_id, 
     posts.post_b_id, 
     posts.post_title, 
     posts.post_cont, 
     posts.thumb, 
     posts.post_user, 
     boards.board_title_l, 
     boards.board_title 
FROM (SELECT post_id 
     FROM posts 
       JOIN follow 
       ON posts.post_b_id = follow.board_id 
     WHERE follow.user_id = 1 
     ORDER BY post_id DESC 
     LIMIT 10) sq 
     JOIN posts 
     ON posts.post_id = sq.post_id 
     JOIN boards 
     ON boards.board_id = posts.post_b_id 

Zauważ, że pomijam ORDER BY posts.post_id DESC z zewnętrznego zapytania, ponieważ zazwyczaj szybciej jest sortować końcowy wynik w kodzie niż sortowanie przy użyciu kwerendy MySQL (MySQL często używa do tego celu filesort).

P.S. Możesz zastąpić unikalny klucz w tabeli follow kluczem podstawowym.

+0

Wykonaj klucz podstawowy. Przetestowane zapytanie, stabilna prędkość bez względu na to, gdzie są posty, nie tak szybkie, jak oryginalne zapytanie bez zamówienia, ale być może nie jest to możliwe. – Vixxs

+0

@Vixxs, nieznacznie zmieniłem zapytanie. To powinno być nieco szybsze. Ta wersja zakłada, że ​​kluczem podstawowym 'posts' jest po prostu' (post_id) ', ale nie' (post_id, post_b_id) '. – dened

+0

@Vixxs, proszę nie zapomnieć oznaczyć odpowiedzi jako zaakceptowanej, jeśli pomogło to w rozwiązaniu problemu. Zobacz [tutaj] (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). Możesz także przekazać każdą odpowiedź, jeśli była dla ciebie pomocna. Zobacz [tutaj] (http://stackoverflow.com/help/privileges/vote-up). – dened

0

Zwiększenie parametru sort_buffer_size zwiększy ilość pamięci używanej przez MySQL przed odwołaniem się do tymczasowego pliku na dysku i powinno znacznie pomóc.

+0

Nie ma szczęścia, że ​​wszystkie te tabele mają razem mniej niż 250kb, ja zwiększam d sort_buffer_size i inne bufory (od 1mb) do 16mb, na xampp, te same wyniki. – Vixxs