Le singole query sono più veloci dei join?


44

Domanda concettuale: sono singole query più velocemente di quanto si unisce, o: Dovrei provare a spremere ogni informazioni che voglio sul lato client in una SELECT o semplicemente usare come molti come sembra conveniente?

TL; DR : se la mia query unita richiede più tempo dell'esecuzione di singole query, è colpa mia o è prevedibile?

Innanzitutto, non sono molto esperto di database, quindi posso essere solo io, ma ho notato che quando devo ottenere informazioni da più tabelle, è "spesso" più veloce ottenere queste informazioni tramite più query su singole tabelle (forse contenente un semplice join interno) e mettere insieme i dati sul lato client per provare a scrivere una query (complessa) unita in cui posso ottenere tutti i dati in una query.

Ho provato a mettere insieme un esempio estremamente semplice:

SQL Fiddle

Schema Setup :

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

Query A :

select NAME from MASTER
where ID = 1

Risultati :

| NAME |
--------
|  One |

Quesito B :

select ID, VALUE from DATA
where MASTER_ID = 1

Risultati :

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

Query C :

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

Risultati :

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

Naturalmente, non ho misurato alcuna performance con questi, ma si può osservare:

  • La query A + B restituisce la stessa quantità di informazioni utilizzabili della query C.
  • A + B deve restituire 1 + 2x3 == 7 "Celle dati" al client
  • C deve restituire 3x3 == 9 "Data Cells" al client, perché con il join includo naturalmente una ridondanza nel set di risultati.

Generalizzando da questo (per quanto recuperabile):

Una query unita deve sempre restituire più dati delle singole query che ricevono la stessa quantità di informazioni. Poiché il database deve mettere insieme i dati, per insiemi di dati di grandi dimensioni si può presumere che il database debba svolgere più lavoro su una singola query unita rispetto a quelle individuali, poiché (almeno) deve restituire più dati al client.

Ne conseguirebbe che, quando osservo che suddividere una query sul lato client in più query produce prestazioni migliori, questa è la strada da percorrere o significherebbe piuttosto che ho incasinato la query unita?


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Jack Douglas

1
Ho eseguito un benchmark e pubblicato i risultati in un articolo su Medium . Avrei aggiunto una risposta qui, ma l' ho già fatto su un'altra domanda , e postare la stessa risposta a più domande è disapprovato .
Benjamin

Risposte:


45

Le singole query sono più veloci dei join o: Devo provare a comprimere tutte le informazioni che desidero sul lato client in un'istruzione SELECT o utilizzarne solo quelle che sembrano convenienti?

In qualsiasi scenario prestazionale, è necessario testare e misurare le soluzioni per vedere quale è più veloce .

Detto questo, è quasi sempre il caso che un set di risultati unito da un database correttamente ottimizzato sia più veloce e ridimensiona meglio che restituire le righe di origine al client e quindi unirle lì. In particolare, se i set di input sono grandi e il set di risultati è piccolo, considerare la seguente query nel contesto di entrambe le strategie: unire due tabelle da 5 GB ciascuna, con un set di risultati di 100 righe. È un estremo, ma vedi il mio punto.

Ho notato che quando devo ottenere informazioni da più tabelle, è "spesso" più veloce ottenere queste informazioni tramite più query su singole tabelle (magari contenenti un semplice join interno) e mettere insieme i dati sul lato client da provare per scrivere una query (complessa) unita in cui posso ottenere tutti i dati in una query.

È molto probabile che lo schema o gli indici del database possano essere migliorati per servire meglio le query che ci stai lanciando.

Una query unita deve sempre restituire più dati delle singole query che ricevono la stessa quantità di informazioni.

Di solito non è così. Il più delle volte anche se i set di input sono grandi, il set di risultati sarà molto più piccolo della somma degli input.

A seconda dell'applicazione, i set di risultati di query di grandi dimensioni restituiti al client sono una bandiera rossa immediata: cosa sta facendo il client con un set di dati così grande che non può essere eseguito più vicino al database? La visualizzazione di 1.000.000 di righe a un utente è a dir poco sospetto. Anche la larghezza di banda della rete è una risorsa limitata.

Poiché il database deve mettere insieme i dati, per insiemi di dati di grandi dimensioni si può presumere che il database debba svolgere più lavoro su una singola query unita rispetto a quelle individuali, poiché (almeno) deve restituire più dati al client.

Non necessariamente. Se i dati vengono indicizzati correttamente, è più probabile che l'operazione di join venga eseguita in modo più efficiente nel database senza dover eseguire la scansione di una grande quantità di dati. Inoltre, i motori di database relazionali sono appositamente ottimizzati a basso livello per l'unione ; gli stack client non lo sono.

Ne conseguirebbe che, quando osservo che suddividere una query sul lato client in più query produce prestazioni migliori, questa è la strada da percorrere o significherebbe piuttosto che ho incasinato la query unita?

Dato che hai detto di non avere esperienza quando si tratta di database, suggerirei di saperne di più sulla progettazione del database e sul tuning delle prestazioni. Sono abbastanza sicuro che è qui che si trova il problema. Sono possibili anche query SQL scritte in modo inefficiente, ma con un semplice schema che ha meno probabilità di essere un problema.

Ora, questo non vuol dire che non ci sono altri modi per migliorare le prestazioni. Esistono scenari in cui è possibile scegliere di eseguire la scansione di un insieme di dati medio-grandi e restituirli al client se si intende utilizzare una sorta di meccanismo di memorizzazione nella cache. La memorizzazione nella cache può essere eccezionale, ma introduce complessità nel design. La memorizzazione nella cache potrebbe non essere adatta alla tua applicazione.

Una cosa che non è stata menzionata da nessuna parte è mantenere la coerenza nei dati restituiti dal database. Se vengono utilizzate query separate, è più probabile (a causa di molti fattori) che vengano restituiti dati incoerenti, a meno che non venga utilizzata una forma di isolamento dello snapshot per ogni serie di query.


+1 per la larghezza di banda della rete è anche una risorsa limitata.
Hari Harker,

OP afferma che i set di risultati dei dati JOINed sono sempre più grandi. > Una query unita deve sempre restituire più dati delle singole query. Penso che questo sia oggettivamente vero (per> =), ad esempio i set di risultati differiscono per dimensioni, quindi più dati sul filo. Hai un esempio in cui questo non è vero? Se mi unisco a Autori -> Messaggi e autori ha un campo chiamato "biografia" che è un campo JSON da 1 MB, per un autore di 100 messaggi, attraverso il filo trasmetterò 100 MB contro 1 MB. È sbagliato?
hytromo,

6

Ovviamente, non ho misurato alcuna performance con questi

Hai messo insieme un buon codice di esempio. Hai guardato i tempi in SQL Fiddle? Anche alcuni brevi test non scientifici delle prestazioni mostreranno che la query tre nella tua dimostrazione impiega circa lo stesso tempo per essere eseguita come query uno o due separatamente. Uno e due combinati impiegano circa il doppio di tre e cioè prima che venga eseguito qualsiasi join lato client.

Quando si aumentano i dati, la velocità della query uno e due divergerebbe, ma l'unione del database sarebbe ancora più veloce.

Dovresti anche considerare cosa succederebbe se il join interno eliminasse i dati.


2

Anche l'ottimizzatore delle query dovrebbe essere considerato. Il suo ruolo è prendere il tuo SQL dichiarativo e tradurlo in passaggi procedurali. Per trovare la combinazione più efficiente di passaggi procedurali, esaminerà le combinazioni di utilizzo dell'indice, ordinamenti, memorizzazione nella cache di insiemi di risultati intermedi e ogni sorta di altre cose. Il numero di permutazioni può aumentare notevolmente anche con quelle che sembrano query abbastanza semplici.

Gran parte del calcolo fatto per trovare il piano migliore è guidato dalla distribuzione dei dati all'interno delle tabelle. Queste distribuzioni vengono campionate e archiviate come oggetti statistici. Se questi sono sbagliati, portano l'ottimizzatore a fare scelte sbagliate. Le cattive scelte all'inizio del piano portano a scelte ancora più povere in seguito con un effetto a palle di neve.

Non è sconosciuto per una query di medie dimensioni che restituisce modeste quantità di dati che richiedono minuti per l'esecuzione. Una corretta indicizzazione e buone statistiche riducono quindi questo valore in millisecondi.


-3

Query multiple è la strada da percorrere. Se gestisci scenari semplici come quello, l'overhead dei costi di Query Optimizer è un fattore. Con più dati, arriva l'inefficienza della rete del join (righe ridondanti). Solo con molti più dati si ottiene efficienza.

Alla fine, ciò che provi è qualcosa che molti sviluppatori vedono. I DBA dicono sempre "no, crea un join" ma la realtà è: in questo caso è più veloce effettuare più selezioni semplici.


5
Non c'è "inefficienza della rete" in un join - succede tutto sul server di database, quindi non c'è alcuna rete coinvolta (a meno che non ti stia unendo tramite un collegamento db!)
Chris Saxon,

2
Potresti considerare se il livello di rete ha compressione o meno. Oracle SQL * Net lo fa, in quanto i valori che si ripetono nella stessa colonna vengono compressi in modo efficiente.
David Aldridge,

3
@TomTom potresti avere un punto o meno (come indica David Aldridge, la compressione è importante) ma la tua formulazione è confusa. "inefficienza della rete del join" ? Davvero, correggilo, quindi è ovvio cosa intendi.
ypercubeᵀᴹ

@ChrisSaxon certo che c'è, immagine hai tabelle per un rapporto "title-> base-> table-lines" e hai bisogno di tutte le righe in modo da unirti all'interno di queste 3 tabelle. Ogni tabella ha varchar lunghi quindi ciò che accade è per ogni riga che stai ripetendo questi varchar lunghi. Il livello dell'applicazione deve allocare memoria per tutte queste stringhe e quindi raggrupparle per il modello. Quindi penso che questo significhi, ci sono più dati inviati
MIKE

@MIKE che dipende dalle espressioni selezionate, non dal join. E potrebbe esserci una compressione di rete. Nel database Oracle SQL * Net rimuove i valori duplicati ripetuti nicetheory.io/2018/01/11/…
Chris Saxon,
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.