2015-05-23 24 views
7

Ho letto altri post ma non sono riuscito a risolvere la mia richiesta.Evita filesort con INNER JOIN + ORDER BY

Utilizzando DESC ordinare la query è x20 volte più lento, devo migliorarlo. Questa è la query:

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 

E queste sono le tabelle (Aggiornato):

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; 

Uso ordine predefinito ASC utilizza solo indice e dove, con DESC utilizza indice, in cui, temporaneo e filesort.

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 

Come posso fare la domanda che riceve i risultati in ordine DESC senza filesort e temporaneo.

UPDATE: Ho fatto una nuova query, non temporanea o FileSort, ma Tipo: indice, filtrato: 7.340,00. Quasi veloce come l'ordine ASC se i post sono alla fine della tabella, ma lento se i post che si stanno cercando sono all'inizio. Quindi sembra meglio ma non è abbastanza.

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 

Spiega:

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: Spiegare per la query da 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 

Volte:

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 

Nota interessante, se aggiungo ORDER BY ASC è lento come DESC.

Modificare l'ordine del tavolo sarà un modo divino, ma come ho detto nei commenti non ero in grado di farlo.

+1

Questo sembra buono.- ( – Strawberry

+0

Puoi aggiungere un indice discendente su post_id – Martijn

+0

"InnoDB ordina sempre le righe della tabella secondo l'indice cluster." Ho provato ALTER TABLE 'posts' ORDER BY' post_id' DESC e rimane ASC comunque, altri motori possono avere l'ordine DESC ma se si inserisce o si elimina una riga non viene mantenuto l'ordine – Vixxs

risposta

1

Puoi aiutare MySQL ottimizzatore spostando tutto il lavoro di filtraggio per una sottoquery che cosa accede a soli indici (manipolazione di indici di solito è molto più veloce di manipolare altri dati), e il recupero resto dei dati nella query più esterno:

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 

Nota che ometto lo ORDER BY posts.post_id DESC dalla query esterna, perché di solito è più rapido ordinare il risultato finale nel codice piuttosto che ordinare utilizzando una query MySQL (MySQL usa spesso filesort per quello).

P.S. È possibile sostituire la chiave univoca nella tabella follow con una chiave primaria.

+0

Seguire la chiave primaria. Testato la query, velocità stabile indipendentemente da dove si trovino i post, non veloce quanto la query originale, nessun ordine, ma forse questo non è possibile. – Vixxs

+0

@Vixxs, ho modificato leggermente la query. Questo dovrebbe essere un po 'più veloce. Questa versione presuppone che la chiave primaria di 'posts' sia semplicemente' (post_id) ', ma non' (post_id, post_b_id) '. – dened

+0

@Vixxs, per favore non dimenticare di contrassegnare la risposta come accettata se ha aiutato a risolvere il tuo problema. Vedi [qui] (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). Puoi anche modificare le risposte se è stato utile per te. Vedi [qui] (http://stackoverflow.com/help/privileges/vote-up). – dened

0

L'aumento del parametro sort_buffer_size aumenterà la quantità di memoria utilizzata da MySQL prima di ricorrere a un file su disco temporaneo e dovrebbe essere di notevole aiuto.

+0

Non fortunato con questo, tutte queste tabelle sono meno di 250kb insieme, aumento d il sort_buffer_size e altri buffer (da 1mb) a 16mb, su xampp, stessi risultati. – Vixxs