Cuándo usar STRAIGHT_JOIN con MySQL

88

Acabo de tener una consulta bastante compleja con la que estaba trabajando y tardaba 8 segundos en ejecutarse. EXPLAIN mostraba un orden de tabla extraño y mis índices no se usaban todos, incluso con la sugerencia FORCE INDEX. Encontré la palabra clave de unión STRAIGHT_JOIN y comencé a reemplazar algunas de mis palabras clave INNER JOIN con ella. Noté una mejora considerable en la velocidad. Finalmente, reemplacé todas mis palabras clave INNER JOIN con STRAIGHT_JOIN para esta consulta y ahora se ejecuta en 0.01 segundos.

Mi pregunta es ¿cuándo usas STRAIGHT_JOIN y cuándo usas INNER JOIN? ¿Hay alguna razón para no usar STRAIGHT_JOIN si está escribiendo buenas consultas?

Greg
fuente

Respuestas:

73

No recomendaría usar STRAIGHT_JOIN sin una buena razón. Mi propia experiencia es que el optimizador de consultas de MySQL elige un plan de consulta deficiente con más frecuencia de lo que me gustaría, pero no con la frecuencia suficiente para que deba omitirlo en general, que es lo que haría si siempre usara STRAIGHT_JOIN.

Mi recomendación es dejar todas las consultas como JOIN regulares. Si descubre que una consulta está utilizando un plan de consulta subóptimo, le sugiero que primero intente reescribir o reestructurar un poco la consulta para ver si el optimizador elegirá un plan de consulta mejor. Además, para innodb al menos, asegúrese de que no se trate solo de que las estadísticas de su índice estén desactualizadas ( ANALIZAR TABLA ). Eso puede hacer que el optimizador elija un plan de consulta deficiente. Las sugerencias del optimizador deberían ser generalmente su último recurso.

Otra razón para no usar sugerencias de consulta es que su distribución de datos puede cambiar con el tiempo, o su selectividad de índice puede cambiar, etc. a medida que crece su tabla. Sus sugerencias de consulta que son óptimas ahora, pueden volverse subóptimas con el tiempo. Pero el optimizador no podrá adaptar el plan de consulta debido a sus sugerencias ahora desactualizadas. Se mantiene más flexible si permite que el optimizador tome las decisiones.

nathan
fuente
59
Esta respuesta en realidad no explica cuándo usarla straight_join .
Pacerier
23

De la referencia de MySQL JOIN :

"STRAIGHT_JOIN es similar a JOIN, excepto que la tabla de la izquierda siempre se lee antes que la tabla de la derecha. Esto se puede usar para aquellos (pocos) casos en los que el optimizador de combinación coloca las tablas en el orden incorrecto".

jjclarkson
fuente
27
Gracias, pero ya leí el manual de MySQL. Esperando alguna explicación adicional.
Greg
20

Aquí hay un escenario que surgió recientemente en el trabajo.

Considere tres tablas, A, B, C.

A tiene 3000 filas; B tiene 300.000.000 filas; y C tiene 2000 filas.

Se definen claves externas: B (a_id), B (c_id).

Suponga que tiene una consulta similar a esta:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

En mi experiencia, MySQL puede optar por ir C -> B -> A en este caso. C es más pequeño que A y B es enorme, y todos son equijoins.

El problema es que MySQL no necesariamente tiene en cuenta el tamaño de la intersección entre (C.id y B.c_id) vs (A.id y B.a_id). Si la unión entre B y C devuelve tantas filas como B, entonces es una muy mala elección; si comenzar con A hubiera filtrado B a tantas filas como A, entonces habría sido una opción mucho mejor. straight_joinpodría usarse para forzar este orden así:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

Ahora adebe unirse antes b.

Por lo general, desea realizar las uniones en un orden que minimice el número de filas en el conjunto resultante. Entonces, comenzar con una mesa pequeña y unir de manera que la unión resultante también sea pequeña, es ideal. Las cosas van en forma de pera si se comienza con una mesa pequeña y se une a una mesa más grande y termina tan grande como la mesa grande.

Sin embargo, depende de las estadísticas. Si cambia la distribución de datos, el cálculo puede cambiar. También depende de los detalles de implementación del mecanismo de combinación.

Los peores casos que he visto para MySQL en los que todos los straight_joinindicios de índice, excepto los obligatorios o agresivos, son consultas que paginan una gran cantidad de datos en un orden de clasificación estricto con filtrado ligero. MySQL prefiere usar índices para cualquier filtro y se une sobre ordenaciones; esto tiene sentido porque la mayoría de las personas no están tratando de ordenar toda la base de datos, sino que tienen un subconjunto limitado de filas que responden a la consulta, y ordenar un subconjunto limitado es mucho más rápido que filtrar toda la tabla, sin importar si está ordenada o no. En este caso, poner una combinación recta inmediatamente después de la tabla que tenía la columna indexada que quería ordenar en cosas fijas.

Barry Kelly
fuente
¿Cómo utilizaría la unión directa para solucionar el problema?
Hannele
@Hannele straight_joinevalúa la tabla izquierda antes que la derecha. Entonces, si desea partir A -> B -> Cde mi ejemplo, la primera joinpalabra clave podría reemplazarse con straight_join.
Barry Kelly
Ah, genial. Sería útil incluir eso como ejemplo en su respuesta :)
Hannele
18

MySQL no es necesariamente bueno para elegir el orden de unión en consultas complejas. Al especificar una consulta compleja como straight_join, la consulta ejecuta las uniones en el orden en que se especifican. Si coloca la tabla para que sea el mínimo común denominador primero y especifique straight_join, puede mejorar el rendimiento de la consulta.

IAdapter
fuente
11

STRAIGHT_JOIN, utilizando esta cláusula, puede controlar el JOINorden: qué tabla se escanea en el ciclo exterior y cuál está en el ciclo interior.

Mitendra
fuente
¿Qué son el bucle externo y el bucle interno?
Istiaque Ahmed
Las tablas de @IstiaqueAhmed están unidas por bucles anidados (tome la primera fila de la tabla A y la tabla de lanzamiento del bucle B, luego tome la segunda fila ... y así sucesivamente. Aquí la tabla A está en el bucle exterior)
Contador م
6

Te diré por qué tuve que usar STRAIGHT_JOIN:

  • Tuve un problema de rendimiento con una consulta.
  • Simplificando la consulta, la consulta fue repentinamente más eficiente
  • Tratando de averiguar qué parte específica estaba provocando el problema, simplemente no pude. (2 uniones izquierdas juntas fueron lentas y cada una fue independientemente rápida)
  • Luego ejecuté EXPLAIN con una consulta lenta y rápida (agregando una de las combinaciones de la izquierda)
  • Sorprendentemente, MySQL cambió por completo las órdenes JOIN entre las 2 consultas.

Por lo tanto, forcé a una de las combinaciones a ser straight_join para FORZAR que la combinación anterior se lea primero. ¡Esto impidió que MySQL cambiara el orden de ejecución y funcionó de maravilla!

Nicolas Thery
fuente
2

En mi corta experiencia, una de las situaciones que STRAIGHT_JOINha reducido mi consulta de 30 segundos a 100 milisegundos es que la primera tabla del plan de ejecución no era la tabla que tiene el orden por columnas.

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

SI el optimizador elige presionar stores primero , causará Using index; Using temporary; Using filesortporque

si ORDER BY o GROUP BY contiene columnas de tablas distintas de la primera tabla en la cola de combinación, se crea una tabla temporal.

fuente

aquí el optimizador necesita un poco de ayuda diciéndole que presione salesprimero usando

sales STRAIGHT_JOIN stores
Contador م
fuente
1
(Me embellecí con tu respuesta.)
Rick James
2

Si su consulta termina con ORDER BY... LIMIT..., puede ser óptimo reformular la consulta para engañar al optimizador para que haga lo LIMIT antes del JOIN.

(Esta respuesta no se aplica solo a la pregunta original sobre STRAIGHT_JOIN, ni se aplica a todos los casos de STRAIGHT_JOIN).

Comenzando con el ejemplo de @Accountant م , esto debería ejecutarse más rápido en la mayoría de las situaciones. (Y evita necesitar pistas).

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

Notas:

  • Primero, se obtienen 50 ID. Esto será especialmente rápido con INDEX(date, id).
  • Luego, la unión de nuevo a le salespermite obtener solo 50 "lo que sea" sin tener que arrastrarlos en una mesa temporal.
  • dado que una subconsulta está, por definición, desordenada, ORDER BYdebe repetirse en la consulta externa. (El Optimizador puede encontrar una manera de evitar hacer otro tipo).
  • Sí, es más desordenado. Pero suele ser más rápido.

Me opongo a usar hits porque "Incluso si es más rápido hoy, puede que no sea más rápido mañana".

Rick James
fuente
0

Sé que es un poco antiguo, pero aquí hay un escenario, he estado haciendo un script por lotes para completar una determinada tabla. En algún momento, la consulta fue muy lenta. Parece que el orden de unión era incorrecto en registros particulares:

  • En el orden correcto

ingrese la descripción de la imagen aquí

  • Incrementar la identificación en 1 estropea el orden. Observe el campo 'Extra'

ingrese la descripción de la imagen aquí

  • El uso de straight_join soluciona el problema

ingrese la descripción de la imagen aquí

El orden incorrecto se ejecuta durante aproximadamente 65 segundos mientras que el uso de straight_join se ejecuta en milisegundos

rai
fuente
-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000
lhs295988029
fuente
3
Esto no le brinda suficiente información para determinar cuándo son apropiadas las uniones rectas.
Hannele