¿Por qué LIKE es más de 4 veces más rápido que MATCH ... CONTRA un índice FULLTEXT en MySQL?


12

No entiendo esto.

Tengo una tabla con estos índices.

PRIMARY     post_id
INDEX       topic_id
FULLTEXT    post_text

La tabla tiene (solo) 346 000 filas. Estoy tratando de realizar 2 consultas.

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')

toma 4.05 segundos mientras

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%')

toma 0.027 segundos.

EXPLAIN muestra que la única diferencia está en fulltextposible_keys ( tiene post_text incluido, LIKEno)

Eso es realmente extraño

¿Qué hay detrás de esto? ¿Qué está pasando en el fondo? ¿Cómo puede LIKEser tan rápido cuando no se usa el índice y FULLTEXT tan lento cuando se usa su índice?

ACTUALIZACIÓN1:

En realidad, ahora toma alrededor de 0,5 segundos, tal vez la mesa estaba bloqueada, pero aún así, cuando enciendo el perfil, se muestra que la INICIALIZACIÓN DE FULLTEXT tardó 0.2 segundos. ¿Qué pasa?

Puedo consultar mi tabla con LIKE10x por segundo, con texto completo solo 2x

ACTUALIZACIÓN2:

¡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)

así que pregunto, ¿cómo es esto posible?

Adicionalmente,

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

es realmente lento ¿Se puede romper el texto completo?

ACTUALIZACIÓN3:

¿Que demonios?

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

toma 0.27s mientras

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

¡Lleva más de 30 segundos! ¿Qué está pasando mal aquí?


¿Los tiempos de respuesta entre los dos son consistentes en varias ejecuciones? Estoy tentado a pensar que el almacenamiento en caché del disco puede estar en juego cuando una primera prueba "lenta" carga todos los datos necesarios en la memoria RAM, por lo que la segunda consulta "rápida" es, muy rápido.
atxdba

Pruebe las consultas solo con SQL_NO_CACHE .
mgutt

Esta es una pregunta / respuesta bastante antigua. ¿Algún avance de mysql / mariadb desde esos días?
Roman Susi

1
Precaución: el momento de estas preguntas y respuestas implica que solo se trata de MyISAM. Su aplicabilidad a InnoDB está en duda.
Rick James

@RomanSusi - ¿Le gustaría comenzar una nueva pregunta dirigida a InnoDB?
Rick James

Respuestas:


2

Creo que el problema puede deberse a la presencia del índice FULLTEXT en sí.

Cada vez que hay una consulta que involucra un índice FULLTEXT, MySQL Query Optimizer tiende a incluir la consulta en un escaneo completo de la tabla. He visto esto a lo largo de los años. También escribí una publicación anterior sobre este comportamiento más insignificante en los índices FULLTEXT .

Es posible que deba hacer dos cosas:

  1. refactorice la consulta para que el índice FULLTEXT no arroje al Optimizador de consultas MySQL a un estado de confusión
  2. Agregue un índice adicional que admitirá correctamente la consulta refactorizada

REFACTOR LA CONSULTA

Aquí está tu consulta original

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') 

Deberá refactorizar la consulta de esta manera:

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);

CREAR UN NUEVO ÍNDICE

Necesitará un índice para soportar subqueryA. Ya tienes un índice en topic_id. Debe reemplazarlo de la siguiente manera:

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

Darle una oportunidad !!!

ACTUALIZACIÓN 2012-03-19 13:08 EDT

Prueba este primero

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

Si esto se ejecuta rápidamente y devuelve un pequeño número de filas, intente con esta subconsulta anidada:

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');

ACTUALIZACIÓN 2012-03-19 13:11 EDT

Compare el tiempo de ejecución de esto:

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

con este

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

Si el tiempo de ejecución es el mismo, entonces la cláusula MATCH se está ejecutando en cada fila. Como mencioné anteriormente, el uso de índices FULLTEXT tiende a anular cualquier beneficio que intente y contribuya MySQL Query Optimizer.


Entonces, ¿quiere decir que mi consulta realmente escanea toda la tabla porque topic_id y la post_idconfunde? ¿Por qué la consulta LIKE funciona incluso sin tener índice en estas columnas (topic_id, post_id)? ¿Por qué MYSQL no solo selecciona de forma inteligente topic_id = 144017 AND post_id != 155352y luego solo navega a través de estos resultados? ¿Y qué pasa si 100k filas incluyen mi cadena de búsqueda de texto completo post_text? ¿No los seleccionaría a todos?
Génesis

En realidad estoy confundido aún más. COMO '% text%' tampoco usa índices, significa que escanea toda la tabla, entonces ¿por qué es tan rápido?
Génesis

Mire mi ACTUALIZACIÓN , creo que lo resolverá realmente rápido. Te voy a dar mi representante si lo resuelves.
génesis

Respondiendo a tu segunda actualización. La segunda consulta se ejecutó en menos de 0.01 ms, la primera no terminó. ¿Por qué dijo "Si el tiempo de ejecución es el mismo, entonces la cláusula MATCH se está ejecutando en cada fila". ? ¿No es exactamente lo contrario de lo que debería ser? Si miras aquí , verás que no soy el único con este problema
génesis

Respondiendo a tu primera actualización. La primera consulta se ejecutó en 0.01ms, 0 filas, la segunda devolvió "No se puede encontrar el índice FULLTEXT que coincida con la lista de columnas". Sin embargo, su consulta con 2 subconsultas funciona perfectamente.
génesis
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.