Quando utilizzare STRAIGHT_JOIN con MySQL


88

Ho appena avuto una query abbastanza complessa su cui stavo lavorando e ci sono voluti 8 secondi per l'esecuzione. EXPLAIN mostrava uno strano ordine di tabella e i miei indici non venivano utilizzati tutti anche con il suggerimento FORCE INDEX. Mi sono imbattuto nella parola chiave STRAIGHT_JOIN join e ho iniziato a sostituire alcune delle mie parole chiave INNER JOIN con essa. Ho notato un notevole miglioramento della velocità. Alla fine ho appena sostituito tutte le mie parole chiave INNER JOIN con STRAIGHT_JOIN per questa query e ora viene eseguita in 0,01 secondi.

La mia domanda è quando usi STRAIGHT_JOIN e quando usi INNER JOIN? C'è qualche motivo per non utilizzare STRAIGHT_JOIN se stai scrivendo buone query?

Risposte:


73

Non consiglierei di usare STRAIGHT_JOIN senza una buona ragione. La mia esperienza personale è che l'ottimizzatore di query MySQL sceglie un piano di query scadente più spesso di quanto vorrei, ma non abbastanza spesso da ignorarlo in generale, che è quello che faresti se usassi sempre STRAIGHT_JOIN.

Il mio consiglio è di lasciare tutte le query come JOIN regolari. Se scopri che una query utilizza un piano di query subottimale, ti suggerirei di provare prima a riscrivere o riformulare un po 'la query per vedere se l'ottimizzatore sceglierà un piano di query migliore. Inoltre, almeno per innodb, assicurati che non sia solo che le statistiche dell'indice non siano aggiornate ( ANALYZE TABLE ). Ciò può indurre l'ottimizzatore a scegliere un piano di query scadente. I suggerimenti dell'ottimizzatore dovrebbero generalmente essere l'ultima risorsa.

Un altro motivo per non utilizzare i suggerimenti per le query è che la distribuzione dei dati potrebbe cambiare nel tempo o la selettività dell'indice potrebbe cambiare, ecc. Man mano che la tabella cresce. I suggerimenti per la query che sono ottimali al momento, potrebbero diventare subottimali nel tempo. Ma l'ottimizzatore non sarà in grado di adattare il piano di query a causa dei tuoi suggerimenti ormai obsoleti. Rimani più flessibile se consenti all'ottimizzatore di prendere le decisioni.


59
Questa risposta in realtà non spiega quando usare straight_join .
Pacerier

23

Dal riferimento a MySQL JOIN :

"STRAIGHT_JOIN è simile a JOIN, tranne per il fatto che la tabella di sinistra viene sempre letta prima della tabella di destra. Questo può essere utilizzato per quei (pochi) casi in cui l'ottimizzatore di join mette le tabelle nell'ordine sbagliato."


27
Grazie, ma ho già letto il manuale di MySQL su di esso. Sperando in qualche ulteriore spiegazione.
Greg

20

Ecco uno scenario che è emerso proprio di recente al lavoro.

Considera tre tabelle, A, B, C.

A ha 3.000 righe; B ha 300.000.000 di righe; e C ha 2.000 righe.

Le chiavi esterne sono definite: B (a_id), B (c_id).

Supponi di avere una query simile a questa:

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

Nella mia esperienza, MySQL può scegliere di andare C -> B -> A in questo caso. C è più piccolo di A e B è enorme e sono tutti equijoin.

Il problema è che MySQL non tiene necessariamente conto della dimensione dell'intersezione tra (C.id e B.c_id) vs (A.id e B.a_id). Se l'unione tra B e C restituisce tante righe quante B, allora è una scelta molto scarsa; se iniziare con A avesse filtrato B fino a tante righe quante A, sarebbe stata una scelta molto migliore. straight_joinpotrebbe essere usato per forzare questo ordine in questo modo:

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

Ora adeve essere unito prima b.

In genere si desidera eseguire i join in un ordine che riduca al minimo il numero di righe nel set risultante. Quindi, iniziare con un tavolino e unire in modo tale che anche l'unione risultante sia piccola, è l'ideale. Le cose vanno a forma di pera se si inizia con un tavolino e lo si unisce a un tavolo più grande e si finisce con le dimensioni del tavolo grande.

Tuttavia dipende dalle statistiche. Se la distribuzione dei dati cambia, il calcolo potrebbe cambiare. Dipende anche dai dettagli di implementazione del meccanismo di join.

I casi peggiori che ho visto per MySQL in cui tutti i straight_joinsuggerimenti sull'indice tranne quelli richiesti o aggressivi sono query che si impaginano su molti dati in un rigoroso ordinamento con un leggero filtraggio. MySQL preferisce fortemente utilizzare gli indici per qualsiasi filtro e join rispetto agli ordinamenti; questo ha senso perché la maggior parte delle persone non sta cercando di ordinare l'intero database ma ha piuttosto un sottoinsieme limitato di righe che risponde alla query, e l'ordinamento di un sottoinsieme limitato è molto più veloce del filtrare l'intera tabella, indipendentemente dal fatto che sia ordinato o non. In questo caso, mettendo straight join immediatamente dopo la tabella che aveva la colonna indicizzata, volevo ordinare le cose fisse.


Come useresti straight join per risolvere il problema?
Hannele

@Hannele straight_joinvaluta il tavolo di sinistra prima di quello destro. Quindi, se vuoi passare da A -> B -> Cnel mio esempio, la prima joinparola chiave potrebbe essere sostituita con straight_join.
Barry Kelly

Ah pulito. Sarebbe utile includerlo come esempio nella tua risposta :)
Hannele

18

MySQL non è necessariamente bravo a scegliere l'ordine di join in query complesse. Specificando una query complessa come straight_join, la query esegue i join nell'ordine in cui sono specificati. Posizionando prima la tabella come minimo comune denominatore e specificando straight_join, puoi migliorare le prestazioni della query.


11

STRAIGHT_JOIN, utilizzando questa clausola, puoi controllare l' JOINordine: quale tabella viene scansionata nel ciclo esterno e quale è nel ciclo interno.


Cosa sono il ciclo esterno e il ciclo interno?
Istiaque Ahmed

Le tabelle @IstiaqueAhmed sono unite da cicli nidificati (prendi la prima riga dalla tabella A e lancia la tabella B, quindi prendi la seconda riga ... e così via. Qui la tabella A è al ciclo esterno)
Accountant م

6

Ti dirò perché ho dovuto usare STRAIGHT_JOIN:

  • Ho avuto un problema di prestazioni con una query.
  • Semplificando la query, la query è stata improvvisamente più efficiente
  • Cercando di capire quale parte specifica stava portando il problema, non ci sono riuscito. (2 giunti a sinistra insieme erano lenti e ognuno era indipendentemente veloce)
  • Ho quindi eseguito EXPLAIN sia con query lenta che veloce (aggiungi uno dei join a sinistra)
  • Sorprendentemente, MySQL ha cambiato completamente gli ordini JOIN tra le 2 query.

Pertanto ho forzato uno dei join a essere straight_join per FORZARE che il join precedente fosse letto per primo. Ciò ha impedito a MySQL di modificare l'ordine di esecuzione e ha funzionato a meraviglia!


2

Nella mia breve esperienza, una delle situazioni che STRAIGHT_JOINha ridotto la mia query da 30 secondi a 100 millisecondi è che la prima tabella nel piano di esecuzione non era la tabella che ha l'ordine per colonne

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

SE l'ottimizzatore sceglie di premere per stores primo , causerà Using index; Using temporary; Using filesortperché

se ORDER BY o GROUP BY contiene colonne di tabelle diverse dalla prima tabella nella coda di join, viene creata una tabella temporanea.

fonte

qui l'ottimizzatore ha bisogno di un piccolo aiuto dicendogli di premere per salesprimo usando

sales STRAIGHT_JOIN stores

1
(Ho abbellito la tua risposta.)
Rick James,

2

Se la tua query termina con ORDER BY... LIMIT..., potrebbe essere ottimale riformulare la query per indurre l'ottimizzatore a eseguire LIMIT prima delJOIN .

(Questa risposta non si applica solo alla domanda originale su STRAIGHT_JOIN, né si applica a tutti i casi diSTRAIGHT_JOIN .)

A partire dall'esempio di @Accountant م , dovrebbe essere eseguito più velocemente nella maggior parte delle situazioni. (Ed evita di aver bisogno di suggerimenti.)

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;

Appunti:

  • Innanzitutto, vengono recuperati 50 ID. Questo sarà particolarmente veloce con INDEX(date, id).
  • Quindi il join torna a salesti consente di ottenere solo 50 "whatevers" senza trascinarli in un tavolo temporaneo.
  • poiché una sottoquery è, per definizione, non ordinata, ORDER BYdeve essere ripetuta nella query esterna. (L'ottimizzatore potrebbe trovare un modo per evitare di eseguire effettivamente un altro ordinamento.)
  • Sì, è più disordinato. Ma di solito è più veloce.

Sono contrario all'uso di hit perché "Anche se oggi è più veloce, domani potrebbe non esserlo".


0

So che è un po 'vecchio ma ecco uno scenario, ho fatto script batch per popolare una determinata tabella. Ad un certo punto, la query è stata eseguita molto lentamente. Sembra che l'ordine di join non fosse corretto su record particolari:

  • Nell'ordine corretto

inserisci qui la descrizione dell'immagine

  • L'incremento dell'ID di 1 rovina l'ordine. Notare il campo "Extra"

inserisci qui la descrizione dell'immagine

  • L'uso di straight_join risolve il problema

inserisci qui la descrizione dell'immagine

L'ordine errato viene eseguito per circa 65 secondi durante l'utilizzo di straight_join in millisecondi


-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

3
Questo non ti dà abbastanza informazioni per capire quando i straight join sono appropriati.
Hannele
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.