MySQL: Índice cuando se unen a las tablas no se utilizan (Optimización del rendimiento de interrogación)
-
16-10-2019 - |
Pregunta
necesito para optimizar la siguiente consulta:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50
EXPLAIN SELECT me da el siguiente resultado:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where
¿Por qué se saca primero los artículos y luego las entradas del blog? ¿Es porque tienen más entradas entradas del blog? Y ¿cómo puedo mejorar la consulta para que el articlepost puede utilizar un índice?
Actualización: Un índice se encuentra en blogposts.date_created. Extracción de la enfermedad como blogposts.title y la date_published <= NOW () no hace nada.
Cuando quito el " articles.id AS articleid " se puede utilizar el Índice blogpost_id en los artículos ... Suena extraño para mí, alguien sabe por qué? (Porque en realidad lo necesito ..)
La nueva explicar es similar al siguiente:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles index blogpost_id blogpost_id 4 NULL 6915 Using index; Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where
Solución
Me tomó un vistazo más de cerca a la consulta y que podría ser capaz de rediseñar. Esto es lo que quiero decir:
La parte LIMIT 0,50 de la consulta parece estar hecho ocupada en la última consulta.
Se puede mejorar el diseño de la consulta haciendo lo siguiente:
Paso 1) Crear una consulta en línea para recoger solamente las teclas. En este caso, el identificador de entradas del blog.
Paso 2) Imponer cualquier lugar, ORDER BY y GROUP BY cláusulas sobre la consulta en línea con lo que las llaves.
Paso 3) imposta la cláusula LIMIT como el último paso de hacer la consulta en línea.
Paso 4) Únete a la consulta en línea con la mesa de entrada de blog en el caso de que necesite más columnas de entrada de blog como un blostpost TempTable
Paso 5) Formar parte de este nuevo TempTable entrada de blog con la tabla de artículos.
Pasos 1-3 está destinado a crear un TempTable con exactamente 50 filas y incluir el ID blogpost. A continuación, lleve a cabo todas las combinaciones último lugar.
Con estas medidas aplicadas a su búsqueda original, usted debe tener este aspecto:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
SELECT B.*
FROM
(
SELECT id FROM blogposts
WHERE date_published <= NOW()
AND deleted = 0 AND visible = 1
AND title LIKE '%{de}%'
ORDER BY date_created DESC
LIMIT 0,50
) A
INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;
Desde que ha editado la pregunta y declaró que va a borrar LIKE, ahora su consulta debe mirar más a esto:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
SELECT B.*
FROM
(
SELECT id FROM blogposts
WHERE date_published <= NOW()
AND deleted = 0 AND visible = 1
ORDER BY date_created DESC
LIMIT 0,50
) A
INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;
En la frase [cosas omitidas], si no necesita nada de entradas del blog que no sean las teclas, a continuación, la consulta debería tener este aspecto:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
SELECT id FROM blogposts
WHERE date_published <= NOW()
AND deleted = 0 AND visible = 1
ORDER BY date_created DESC
LIMIT 0,50
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id;
CAVEAT
Asegúrese de que usted construye un índice que involucra las columnas borradas, visible y DATE_CREATED de la siguiente manera:
ALTER TABLE blogposts ADD INDEX deleted_visible_date_created (deleted,visible,date_created);
darle una oportunidad !!!
Otros consejos
Su condición donde blogposts.title LIKE '%{de}%'
causará un escaneo completo de tabla en la página tabla entradas del blog. Es probable que exploran figuras MySQL 6915 artículos es más eficiente.
En cuanto a la forma de mejorarlo, es posible añadir un índice en entradas del blog utilizando el date_created
o date_published
y añadir un rango de la a la condición WHERE (algo que no sea <= NOW ())
Y COMO blogposts.title '%} {% de' - Inútil para la optimización (que conduce comodín)
donde blogposts.deleted = 0 Y blogposts.visible = 1 - Yuck:. Si no se deben mostrar, sacarlos de la tabla
Y blogposts.date_published <= NOW () ORDER BY DESC blogposts.date_created LIMIT 0, 50 - que conduce a la necesidad de índice (date_published) (Sin embargo, similares y banderas pueden impedir que este índice sea utilizado.)
Por favor proporcione SHOW CREATE TABLE ...; SHOW TABLE STATUS ...;
Muchas gracias:)
Me lo dio una oportunidad y descubrió que en realidad su último comentario sobre el índice era lo que necesitaba. A veces es sólo una pequeña mejora que se necesita ...;) Comparación:
Antiguo consulta:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM blogposts
JOIN articles ON articles.blogpost_id = blogposts.id
WHERE blogposts.deleted = 0
AND blogposts.title LIKE '%{de}%'
AND blogposts.visible = 1
AND blogposts.date_published <= NOW()
ORDER BY blogposts.date_created DESC
LIMIT 0 , 50
Nueva consulta:
SELECT /* [things omitted] */ articles.blogpost_id, articles.id AS articleid
FROM
(
SELECT B.*
FROM
(
SELECT id FROM blogposts
WHERE date_published <= NOW()
AND deleted = 0 AND visible = 1
AND title LIKE '%{de}%'
ORDER BY date_created DESC
LIMIT 0,50
) A
INNER JOIN blogposts B USING (id)
) blogposts
INNER JOIN articles
ON blogposts.id = articles.blogpost_id
Ahora, sin mencionar el índice:
Antiguo Explicar Resultado:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE articles ALL blogpost_id NULL NULL NULL 6915 Using temporary; Using filesort
1 SIMPLE blogposts eq_ref PRIMARY PRIMARY 4 articles.blogpost_id 1 Using where
Nueva Explicar Resultado:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 50
1 PRIMARY articles ref blogposts_id blogposts_id 4 blogposts.id 1
2 DERIVED <derived3> ALL NULL NULL NULL NULL 50
2 DERIVED B eq_ref PRIMARY PRIMARY 4 A.id 1
3 DERIVED blogposts ALL deleted,visible,date_published deleted 1 28198 Using filesort
Explicar resultado con índice en la consulta de edad:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE blogposts ref PRIMARY,deleted,visible,date_published,deleted_vis... deleted_visible_date_created 2 const,const 27771 Using where
1 SIMPLE articles ref blogposts_id blogposts_id 4 db.blogposts.id 1
Explicar resultado con índice en nueva consulta:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 50
1 PRIMARY articles ref blogposts_id blogposts_id 4 blogposts.id 1
2 DERIVED <derived3> ALL NULL NULL NULL NULL 50
2 DERIVED B eq_ref PRIMARY PRIMARY 4 A.id 1
3 DERIVED blogposts ref deleted,visible,date_published,deleted_visible_dat... deleted_visible_date_created 2 27771 Using where
Velocidad en la consulta de edad sin / con índice: 0,1835 / 0,0037
Velocidad en la nueva consulta sin / con el índice: 0,1883 / 0,0035
Por lo tanto, debido a la differency solo marginal entre la vieja pregunta / nueva prefiero seguir utilizando la consulta de edad, pero con el índice. Pero voy a tener esto en cuenta, como si algún día la consulta de edad es demasiado lento:)
Lo que sería interesante para mí es saber, cómo ha llegado hasta la idea de establecer el índice de esta manera? Creo que he publicado esta pregunta, he intentado también con deleted_visible pero en lugar de DATE_CREATED he usado date_published (ya que está en la cláusula-where) ...
Gracias:)
ACTUALIZACIÓN por RolandoMySQLDBA 2011-05-19 13:03
Lo que dio lejos para mí era el WHERE y ORDER BY cláusulas.
En la cláusula WHERE, suprimido (0) y visible (1) son valores estáticos. En la cláusula ORDER BY, el DATE_CREATED era como un objetivo en movimiento entre todas las filas con borrada = 0 y visible = 1. Así, coloqué las variables estáticas frente al índice de primero, luego el blanco móvil última en el índice. Por lo general, eso es parte del principio subyacente de refactorización sentencias SQL. Es necesario índices que apoyen su WHERE, GROUP BY y ORDER cláusulas.