Perché LIKE è più di 4x più veloce di MATCH ... CONTRO su un indice FULLTEXT in MySQL?


12

Non lo capisco.

Ho un tavolo con questi indici

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

La tabella ha (solo) 346000 righe. Sto cercando di eseguire 2 query.

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id = 144017 
AND post_id != 155352 
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')

richiede 4.05 secondi mentre

SELECT post_id 
FROM phpbb_posts 
WHERE topic_id=144017 
AND post_id != 155352 
AND post_text LIKE ('%http://rapidshare.com/files/5494794/photo.rar%')

richiede 0,027 secondi.

EXPLAIN mostra che l'unica differenza è in possible_keys ( fulltextha incluso post_text, LIKEno)

È davvero strano.

Cosa c'è dietro questo? Cosa sta succedendo in background? Come può LIKEessere così veloce quando non si utilizza index e FULLTEXT così lentamente quando si utilizza il suo indice?

Update1:

In realtà ora ci vogliono circa 0,5 secondi, forse la tabella era bloccata, ma ancora, quando accendo il profiling, mostra che INIZIALIZZAZIONE COMPLETA ha impiegato 0,2 secondi. Che cosa succede?

Posso interrogare la mia tabella con LIKE10 volte al secondo, con testo completo solo 2x

UPDATE2:

Sorpresa!

mysql> SELECT post_id FROM phpbb_posts WHERE post_id != 2 AND topic_id = 6 AND MATCH(post_text) AGAINST ('rapidshare.com');
Empty set (0.04 sec)

quindi sto chiedendo, come è possibile?

Inoltre,

SELECT count(*) FROM phpbb_posts WHERE MATCH(post_text) AGAINST ('rapidshare.com')

è molto lento. Può essere fulltext rotto?

Update3:

Che diavolo?

SELECT forum_id, post_id, topic_id, post_text  FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

impiega 0,27 secondi

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

richiede più di 30 secondi! Cosa non va qui?


I tempi di risposta tra i due sono coerenti su più corse? Sono tentato di pensare che la cache del disco possa entrare in gioco quando un primo test "lento" carica tutti i dati necessari in ram, quindi la seconda query "veloce" è molto veloce.
atxdba,

Testare le query solo con SQL_NO_CACHE .
mgutt,

Questa è una domanda / risposta piuttosto vecchia. Qualche progresso da mysql / mariadb da quei giorni?
Roman Susi,

1
Attenzione: i tempi di queste domande e risposte implicano che sta parlando solo di MyISAM. La sua applicabilità a InnoDB è in discussione.
Rick James,

@RomanSusi - Vuoi iniziare una nuova domanda rivolta a InnoDB?
Rick James,

Risposte:


2

Penso che il problema possa derivare dalla presenza dell'indice FULLTEXT stesso.

Ogni volta che c'è una query che coinvolge un indice FULLTEXT, MySQL Query Optimizer tende a sottoporre a query la query in una scansione completa della tabella. L'ho visto negli anni. Ho anche scritto un post precedente su questo comportamento più insignificante negli indici FULLTEXT .

Potrebbe essere necessario fare due cose:

  1. riformattare la query in modo che l'indice FULLTEXT non getti MySQL Query Optimizer in uno stato di confusione
  2. Aggiungi un indice aggiuntivo che supporterà correttamente la query refactored

RIFORMA IL QUERY

Ecco la tua query originale

SELECT post_id  
FROM phpbb_posts  
WHERE topic_id = 144017  
AND post_id != 155352  
AND MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar') 

Sarà necessario riformattare la query in questo modo:

SELECT subqueryA.post_id
FROM
(
    SELECT post_id FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) subqueryA
INNER JOIN
(
    SELECT post_id FROM phpbb_posts
    WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar')
) subqueryB
USING (post_id);

CREA UN NUOVO INDICE

Avrai bisogno di un indice per supportare subqueryA. Hai già un indice attivo topic_id. È necessario sostituirlo come segue:

ALTER TABLE phpbb_posts ADD INDEX topic_post_ndx (topic_id,post_id);
ALTER TABLE phpbb_posts DROP INDEX topic_id;

Provaci !!!

AGGIORNAMENTO 2012-03-19 13:08 EDT

Prova prima questo

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A;

Se questo funziona veloce e restituisce un piccolo numero di righe, prova questa subquery nidificata:

SELECT post_id FROM
(
    SELECT * FROM phpbb_posts
    WHERE topic_id = 144017
    AND post_id != 155352
) A
WHERE MATCH(post_text) AGAINST('http://rapidshare.com/files/5494794/photo.rar');

AGGIORNAMENTO 2012-03-19 13:11 EDT

Confronta il tempo di esecuzione di questo:

SELECT count(*) FROM phpbb_posts  WHERE MATCH(post_text) AGAINST ('rapidshare.com') LIMIT 0, 30;

con questo

SELECT count(*) FROM phpbb_posts WHERE 1 = 1;

Se il tempo di esecuzione è lo stesso, la clausola MATCH viene eseguita su ogni riga. Come ho già detto in precedenza, l'utilizzo degli indici FULLTEXT tende a annullare tutti i vantaggi tentati e forniti da MySQL Query Optimizer.


Quindi vuoi dire che la mia query in realtà esegue la scansione dell'intera tabella perché topic_id e la post_idconfonde? Perché la query LIKE funziona anche senza l'indice su queste colonne (topic_id, post_id)? Perché MYSQL non seleziona semplicemente in modo intelligente topic_id = 144017 AND post_id != 155352e quindi naviga solo attraverso questi risultati? E se 100k righe includono la mia stringa di ricerca full-text in post_text? Non li selezionerebbe tutti?
Genesi,

In realtà sono ancora più confuso. COME '% text%' non usa neanche gli indici, significa che scansiona l'intera tabella, quindi perché è così veloce?
Genesi,

Per favore, guarda il mio AGGIORNAMENTO , penso che lo risolverai molto velocemente. Ti darò il mio rappresentante se lo risolvi.
Genesi,

In risposta al tuo secondo aggiornamento. La seconda query è stata eseguita in meno di 0,01 ms, la prima non è terminata. Perché hai detto "Se il tempo di esecuzione è lo stesso, la clausola MATCH viene eseguita su ogni riga". ? Non è esattamente l'opposto di quello che dovrebbe essere? Se guardi qui , vedrai che non sono l'unico con questo problema
genesi del

In risposta al tuo primo aggiornamento. La prima query è stata eseguita in 0,01 ms, 0 righe, la seconda ha restituito "Impossibile trovare l'indice FULLTEXT corrispondente all'elenco delle colonne". Tuttavia, la tua query con 2 sottoquery funziona perfettamente!
Genesi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.