2010-07-18 16 views
5

Ho un sito Web in cui i visitatori possono lasciare commenti. Voglio aggiungere la possibilità di rispondere ai commenti (ad esempio commenti nidificati).Ottimizza LEFT JOIN sul tavolo con 30.000+ righe

In un primo momento questa interrogazione è stato veloce, ma dopo che ho popolato la tabella con i commenti esistenti (circa 30000) una semplice query come:

SELECT c.id, c2.id 
    FROM (SELECT id 
     FROM swb_comments 
     WHERE pageId = 1411 
     ORDER BY id DESC 
     LIMIT 10) AS c 
    LEFT JOIN swb_comments AS c2 ON c.id = c2.parentId 

preso più di 2 secondi, senza childComments (!).

Come ottimizzare una query come questa? Sulla soluzione possibile sarebbe http://www.ferdychristant.com/blog//articles/DOMM-7QJPM7 (scorrere fino a "Il modello di tabella flat fatto bene") ma ciò rende l'impaginazione piuttosto difficile (come limitare 10 commenti principali all'interno di 1 query?)

La tabella ha 3 indici, id, pageId e ParentId.

Grazie in anticipo!

MODIFICA:

Definizione tabella aggiunta. Questa è la definizione completa con alcune differenze a quanto sopra query SELECT, (cioè pageId anziché numberId evitare confussion)

CREATE TABLE `swb_comments` (
    `id` mediumint(9) NOT NULL auto_increment, 
    `userId` mediumint(9) unsigned NOT NULL default '0', 
    `numberId` mediumint(9) unsigned default NULL, 
    `orgId` mediumint(9) unsigned default NULL, 
    `author` varchar(100) default NULL, 
    `email` varchar(255) NOT NULL, 
    `message` text NOT NULL, 
    `IP` varchar(40) NOT NULL, 
    `timestamp` varchar(25) NOT NULL, 
    `editedTimestamp` varchar(25) default NULL COMMENT 'last edited timestamp', 
    `status` varchar(20) NOT NULL default 'publish', 
    `parentId` mediumint(9) unsigned NOT NULL default '0', 
    `locale` varchar(10) NOT NULL, 
    PRIMARY KEY (`id`), 
    KEY `userId` (`userId`), 
    KEY `numberId` (`numberId`), 
    KEY `orgId` (`orgId`), 
    KEY `parentId` (`parentId`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=34748 ; 
+0

Avete in programma di eseguire annidamenti illimitati? (ad esempio, puoi rispondere di nuovo a una risposta?) O solo a un livello di profondità massima? –

+2

Cosa dice la SPIEGA? –

+0

L'ID SELECT FROM swb_comments WHERE pageId = 1411 LIMIT 10 è lento? quanti colpi ottieni dall'ID genitore? –

risposta

1

Il problema è che MySQL non può applicare indice se esso necessità di trattare con il risultato di un derivato query (ecco perché hai NULL nella colonna possible_keys). Quindi suggerisco di filtrare i dieci commenti che avete bisogno:

SELECT * FROM swb_comments WHERE pageId = 1411 ORDER BY id DESC LIMIT 10 

E dopo che inviano richiesta separata per ottenere risposte per ogni commento id:

SELECT * FROM swb_comments WHERE parentId IN ($commentId1, $commentId2, ..., $commentId10) 

In questo motore di database caso sarà in grado di applicare gli indici pageId e parentId in modo efficiente.

+0

Grazie! Ci proverò! –

+0

Normalmente, avrei fatto un downvote a questo, ma avendo imbattuto in innumerevoli cose strane con l'ottimizzatore MySQL che la maggior parte degli altri vendor non ha, devo solo annuire con la testa in segno di profonda comprensione. – Joe

0

Se il signor Fedorenko è corretto e la subquery è che causa le difficoltà di ottimizzazione, non si potrebbe provare ...

SELECT c.id, c2.id 
    FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID 
    WHERE c.pageId = 1411 
    ORDER BY c.id DESC 
    LIMIT 10; 

e vedere se si tratta di un miglioramento?

Successivamente, ho creato una tabella utilizzando la definizione, l'ho riempita con 30.000 righe scheletriche e ho provato entrambe le query. Entrambi si completano in un tempo troppo breve per notarlo. I piani di spiegazione sono qui ...

mysql> EXPLAIN SELECT c.id, c2.id 
       FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID 
       WHERE c.numberId = 1411  ORDER BY c.id DESC  LIMIT 10; 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      | 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 
| 1 | SIMPLE  | c  | ref | numberId  | numberId | 4  | const  | 1 | Using where; Using filesort | 
| 1 | SIMPLE  | c2 | ref | parentId  | parentId | 3  | books.c.id | 14 |        | 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 

mysql> EXPLAIN SELECT c.id, c2.id 
        FROM swb_comments c LEFT JOIN swb_comments c2 ON c.id = c2.parentID 
        WHERE c.numberId = 1411  ORDER BY c.id DESC  LIMIT 10; 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      | 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 
| 1 | SIMPLE  | c  | ref | numberId  | numberId | 4  | const  | 1 | Using where; Using filesort | 
| 1 | SIMPLE  | c2 | ref | parentId  | parentId | 3  | books.c.id | 14 |        | 
+----+-------------+-------+------+---------------+----------+---------+------------+------+-----------------------------+ 

e sono esattamente quello che mi aspetterei.

Questo è molto misterioso.

Ci penserò un po 'di più per vedere se c'è qualcos'altro che possiamo provare.

+0

Questa era la mia query originale, ma non supporta l'impaginazione dei commenti principali. E questa query è in effetti molto più lenta, circa 32 secondi. –

+0

Hmm. Questo mi sorprende. Che cosa dice EXPLAIN a riguardo? –

+0

Ho aggiornato http://carl-fredrik.net/explain.html con una EXPLAIN per la query precedente. Penso che "Usare dove; Uso temporaneo; Utilizzo di filesort" sono il colpevole qui. –