Perché i join sono negativi quando si considera la scalabilità?


92

Perché le giunzioni sono cattive o "lente". So di averlo sentito più di una volta. Ho trovato questa citazione

Il problema è che i join sono relativamente lenti, specialmente su set di dati molto grandi, e se sono lenti il ​​tuo sito web è lento. Ci vuole molto tempo per ottenere tutti quei bit separati di informazioni dal disco e rimetterli tutti insieme di nuovo.

fonte

Ho sempre pensato che fossero veloci soprattutto quando cercavo un PK. Perché sono "lenti"?

sql  join 

Risposte:


98

La scalabilità riguarda la pre-elaborazione, la diffusione o la riduzione del lavoro ripetuto all'essenziale, al fine di ridurre al minimo l'uso delle risorse per unità di lavoro. Per ridimensionare bene, non fai nulla di cui non hai bisogno in termini di volume e le cose che fai effettivamente ti assicurano che siano fatte nel modo più efficiente possibile.

In quel contesto, ovviamente l'unione di due origini dati separate è relativamente lenta, almeno rispetto al non unirle, perché è un lavoro che devi svolgere dal punto in cui l'utente lo richiede.

Ma ricorda che l'alternativa non è più avere due dati separati; devi mettere i due punti dati disparati nello stesso record. Non è possibile combinare due diversi pezzi di dati senza una conseguenza da qualche parte, quindi assicurati di aver compreso il compromesso.

La buona notizia è che i database relazionali moderni sono bravi nei join. Non dovresti davvero pensare che i join siano lenti con un buon database usato bene. Esistono diversi modi adatti alla scalabilità per prendere join non elaborati e renderli molto più veloci:

  • Partecipa su una chiave surrogata (colonna autonumer / identità) piuttosto che su una chiave naturale. Ciò significa confronti più piccoli (e quindi più veloci) durante l'operazione di join
  • Indici
  • Viste materializzate / indicizzate (pensala come un join precalcolato o una de-normalizzazione gestita )
  • Colonne calcolate. È possibile utilizzarlo per eseguire l'hashing o in altro modo pre-calcolare le colonne chiave di un join, in modo che quello che sarebbe un confronto complicato per un join ora è molto più piccolo e potenzialmente preindicizzato.
  • Partizioni di tabelle (aiuta con set di dati di grandi dimensioni distribuendo il carico su più dischi o limitando quella che potrebbe essere stata una scansione di tabella a una scansione di partizioni)
  • OLAP (calcola in anticipo i risultati di determinati tipi di query / join. Non è del tutto vero, ma puoi pensare a questo come a una denormalizzazione generica )
  • Replica, gruppi di disponibilità, log shipping o altri meccanismi per consentire a più server di rispondere alle query di lettura per lo stesso database e quindi scalare il carico di lavoro su più server.
  • Utilizzo di un livello di memorizzazione nella cache come Redis per evitare la riesecuzione di query che richiedono join complessi.

Vorrei arrivare a dire che la ragione principale per cui esistono i database relazionali è per consentire di eseguire join in modo efficiente * . Certamente non si tratta solo di memorizzare dati strutturati (potresti farlo con costrutti di file flat come csv o xml). Alcune delle opzioni che ho elencato ti permetteranno persino di creare completamente il tuo join in anticipo, quindi i risultati sono già fatti prima di inviare la query, proprio come se avessi denormalizzato i dati (a costo di operazioni di scrittura più lente).

Se hai un join lento, probabilmente non stai utilizzando correttamente il tuo database.

La denormalizzazione dovrebbe essere eseguita solo dopo che queste altre tecniche hanno fallito. E l'unico modo per giudicare veramente il "fallimento" è fissare obiettivi di prestazione significativi e misurarli rispetto a questi obiettivi. Se non hai misurato, è troppo presto anche solo per pensare alla denormalizzazione.

* Cioè, esistono come entità distinte dalle semplici raccolte di tabelle. Un motivo in più per un vero RDBMS è l'accesso simultaneo sicuro.


14
Gli indici dovrebbero probabilmente essere in cima all'elenco. Molti sviluppatori ( tosse ) sembrano dimenticarsene durante i test su un piccolo set di dati e poi mettono in ginocchio il database in produzione. Ho visto query che vengono eseguite nell'ordine di 100.000 volte più velocemente semplicemente aggiungendo indici. E sono indici arbitrari senza nemmeno fare un'analisi approfondita dei dati per determinare il miglior mix per la corrispondenza del prefisso più a sinistra.
Duncan

Penso di avere l'ordine giusto: è solo che la maggior parte degli sviluppatori fa già il primo elemento, quindi gli indici sono il primo elemento in cui dovranno apportare modifiche.
Joel Coehoorn

Nel tuo terzo elemento, menzioni "Viste materializzate / indicizzate". Stai parlando di viste SQL regolari o qualcos'altro?
slolife

Le normali viste sql @slolife sono come eseguire una query aggiuntiva in background al volo quando si utilizza una query che fa riferimento alla vista. Ma puoi anche dire a sql server di "materializzare" alcune viste. Quando si esegue questa operazione, sql server manterrà una copia extra dei dati della vista, proprio come una normale tabella, in modo tale che quando si fa riferimento alla vista in una query non è più necessario eseguire questa query in background perché i dati sono già presenti . Puoi anche inserire indici diversi nella vista rispetto alla tabella di origine, per ottimizzare ulteriormente le prestazioni.
Joel Coehoorn

Grazie Joel. Dovrò esaminarlo.
slolife

29

Le unioni possono essere più lente rispetto a evitarle tramite la denormalizzazione, ma se usate correttamente (unione su colonne con indici appropriati e così via) non sono intrinsecamente lente .

La denormalizzazione è una delle tante tecniche di ottimizzazione che è possibile prendere in considerazione se lo schema del database ben progettato presenta problemi di prestazioni.


2
... tranne che in MySQL, che sembra avere problemi di prestazioni con un gran numero di join indipendentemente da come appaiono i tuoi indici. O almeno lo ha fatto in passato.
Powerlord

2
Punto preso, se ci sono problemi noti con il DBMS specifico (e forse anche la versione), questo consiglio può avere senso, ma come consiglio generale è piuttosto fuorviante se si utilizza un database relazionale. Detto questo, i meccanismi di archiviazione non relazionali stanno diventando più popolari SimpleDB di Amazon e CouchDB ( couchdb.apache.org ) sono esempi. Se ti sei servito meglio lasciandoti alle spalle il modello relazionale, probabilmente dovresti lasciare i prodotti ottimizzati anche per il dietro e cercare altri strumenti.
Tendayi Mawushe

13

l'articolo dice che sono lenti rispetto all'assenza di join. questo può essere ottenuto con la denormalizzazione. quindi c'è un compromesso tra velocità e normalizzazione. non dimenticare anche l'ottimizzazione prematura :)


anche questa non è una regola rigida, se ti unisci a una tabella, mysql potrebbe usare un indice per eseguire quell'unione - quell'unione dell'indice potrebbe eliminare molte righe e un altro indice per qualsiasi clausola where sulle tabelle. Se non ti iscrivi, mysql userà in genere un solo indice (che potrebbe non essere il più efficiente), indipendentemente da come è formata la clausola where.
leeeroy

11

Innanzitutto, la ragion d'essere (ragion d'essere) di un database relazionale è quella di poter modellare le relazioni tra entità. I join sono semplicemente i meccanismi attraverso i quali attraversiamo queste relazioni. Certamente hanno un costo nominale, ma senza join non c'è davvero alcun motivo per avere un database relazionale.

Nel mondo accademico apprendiamo cose come le varie forme normali (1a, 2a, 3a, Boyce-Codd, ecc.) E apprendiamo diversi tipi di chiavi (primaria, straniera, alternativa, unica, ecc.) E come queste cose si combinano insieme per progettare un database. E apprendiamo i rudimenti di SQL oltre a manipolare sia la struttura che i dati (DDL e DML).

Nel mondo aziendale, molti dei costrutti accademici si sono rivelati sostanzialmente meno praticabili di quanto ci fosse stato fatto credere. Un esempio perfetto è la nozione di chiave primaria. Dal punto di vista accademico è quell'attributo (o insieme di attributi) che identifica in modo univoco una riga nella tabella. Quindi, in molti domini problematici, la chiave primaria accademica corretta è un composto di 3 o 4 attributi. Tuttavia, quasi tutti nel mondo aziendale moderno utilizzano un numero intero sequenziale generato automaticamente come chiave primaria di una tabella. Perché? Due ragioni. Il primo è perché rende il modello molto più pulito durante la migrazione di FK dappertutto. Il secondo, e più pertinente a questa domanda, è che il recupero dei dati tramite i join è più veloce ed efficiente su un singolo intero rispetto a 4 colonne varchar (come già menzionato da alcune persone).

Scaviamo un po 'più a fondo ora in due sottotipi specifici di database del mondo reale. Il primo tipo è un database transazionale. Questa è la base per molte applicazioni di e-commerce o di gestione dei contenuti che guidano i siti moderni. Con un database delle transazioni, stai ottimizzando notevolmente il "throughput delle transazioni". La maggior parte delle app di commercio o di contenuto deve bilanciare le prestazioni delle query (da determinate tabelle) con le prestazioni di inserimento (in altre tabelle), sebbene ogni app avrà i propri problemi aziendali da risolvere.

Il secondo tipo di database del mondo reale è un database di report. Questi vengono utilizzati quasi esclusivamente per aggregare dati aziendali e per generare report aziendali significativi. Hanno in genere una forma diversa rispetto ai database delle transazioni in cui vengono generati i dati e sono altamente ottimizzati per la velocità di caricamento dei dati in blocco (ETL) e le prestazioni delle query con set di dati grandi o complessi.

In ogni caso, lo sviluppatore o l'amministratore di database deve bilanciare attentamente sia la funzionalità che le curve delle prestazioni e ci sono molti trucchi per migliorare le prestazioni su entrambi i lati dell'equazione. In Oracle puoi fare quello che viene chiamato un "piano di spiegazione" in modo da poter vedere nello specifico come una query viene analizzata ed eseguita. Stai cercando di massimizzare il corretto utilizzo degli indici da parte del DB. Un vero e proprio no-no è mettere una funzione nella clausola where di una query. Ogni volta che lo fai, garantisci che Oracle non utilizzerà alcun indice su quella particolare colonna e probabilmente vedrai una scansione completa o parziale della tabella nel piano di spiegazione. Questo è solo un esempio specifico di come potrebbe essere scritta una query che finisce per essere lenta e non ha nulla a che fare con i join.

E mentre parliamo di scansioni di tabelle, ovviamente influiscono sulla velocità delle query in modo proporzionale alle dimensioni della tabella. Una scansione completa della tabella di 100 righe non è nemmeno evidente. Esegui la stessa query su una tabella con 100 milioni di righe e dovrai tornare la prossima settimana per il ritorno.

Parliamo di normalizzazione per un minuto. Questo è un altro argomento accademico ampiamente positivo che può essere stressato. Il più delle volte quando parliamo di normalizzazione, intendiamo davvero l'eliminazione di dati duplicati inserendoli nella propria tabella e migrando un FK. La gente di solito salta l'intera faccenda della dipendenza descritta da 2NF e 3NF. Eppure, in un caso estremo, è certamente possibile avere un database BCNF perfetto che è enorme e una bestia completa per scrivere codice perché è così normalizzato.

Allora dove ci bilanciamo? Non esiste un'unica risposta migliore. Tutte le risposte migliori tendono ad essere un compromesso tra facilità di manutenzione della struttura, facilità di manutenzione dei dati e facilità di creazione / manutenzione del codice. In generale, minore è la duplicazione dei dati, meglio è.

Allora perché i join a volte sono lenti? A volte è un cattivo design relazionale. A volte è un'indicizzazione inefficace. A volte è un problema di volume di dati. A volte è una domanda scritta in modo orribile.

Ci scusiamo per una risposta così prolissa, ma mi sono sentito obbligato a fornire un contesto più carnoso intorno ai miei commenti piuttosto che a snocciolare una risposta di 4 proiettili.


10

Le persone con database di dimensioni terrabyte usano ancora i join, se riescono a farli funzionare in termini di prestazioni, allora puoi farlo anche tu.

Ci sono molte ragioni per non denomalizzare. In primo luogo, la velocità delle query selezionate non è l'unica o addirittura la principale preoccupazione dei database. L'integrità dei dati è la prima preoccupazione. Se denormalizzi, devi mettere in atto tecniche per mantenere i dati denormalizzati man mano che i dati principali cambiano. Quindi supponiamo di voler memorizzare il nome del client in tutte le tabelle invece di unirci alla tabella client su client_Id. Ora, quando il nome del client cambia (probabilità del 100% che alcuni dei nomi dei client cambieranno nel tempo), ora è necessario aggiornare tutti i record figlio per riflettere tale modifica. Se lo fai con un aggiornamento a cascata e hai un milione di record secondari, quanto velocemente supponi che sarà e quanti utenti subiranno problemi di blocco e ritardi nel loro lavoro mentre accade? Inoltre la maggior parte delle persone che denormalizzano perché "

La denormalizzazione è un processo complesso che richiede una conoscenza approfondita delle prestazioni e dell'integrità del database se deve essere eseguita correttamente. Non tentare di denormalizzare se non si dispone di tale esperienza nel personale.

I join sono abbastanza veloci se fai diverse cose. Per prima cosa usa una chiave suggorgate, un int join è quasi sempre il join più veloce. Secondo, indicizza sempre la chiave esterna. Utilizzare tabelle derivate o condizioni di join per creare un set di dati più piccolo su cui filtrare. Se si dispone di un database di grandi dimensioni e molto complesso, assumere una persona di database professionale con esperienza nella suddivisione in partizioni e nella gestione di database enormi. Esistono molte tecniche per migliorare le prestazioni senza eliminare i join.

Se hai solo bisogno di capacità di query, sì, puoi progettare un datawarehouse che può essere denormalizzato e viene popolato tramite uno strumento ETL (ottimizzato per la velocità) e non l'inserimento dei dati dell'utente.


8

I join sono lenti se

  • i dati sono indicizzati in modo non corretto
  • risultati scarsamente filtrati
  • query di partecipazione scritta male
  • set di dati molto grandi e complessi

Quindi, vero, più grandi sono i tuoi set di dati, maggiore sarà l'elaborazione di cui avrai bisogno per una query, ma controllare e lavorare sulle prime tre opzioni di cui sopra spesso produrrà ottimi risultati.

La tua fonte offre la denormalizzazione come opzione. Questo va bene solo finché hai esaurito alternative migliori.


7

I join possono essere lenti se è necessario scansionare grandi porzioni di record da ogni lato.

Come questo:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id

Anche se un indice è definito su account_customer, tutti i record di quest'ultimo devono ancora essere scansionati.

Per l'elenco delle query, gli ottimizzatori decenti probabilmente non considereranno nemmeno il percorso di accesso all'indice, facendo invece a HASH JOINo a MERGE JOIN.

Nota che per una query come questa:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
WHERE   customer_last_name = 'Stellphlug'

l'unione molto probabilmente sarà veloce: prima customer_last_nameverrà usato un indice attivo per filtrare tutti gli Stellphlug (che sono ovviamente non molto numerosi), poi account_customerverrà emesso un indice di scansione per ogni Stellphlug per trovare le sue transazioni.

Nonostante il fatto che questi possano essere miliardi di record in accountse customers, solo pochi dovranno effettivamente essere scansionati.


ma è difficile evitarlo. progetta la tua app in modo che questo tipo di query non venga eseguito troppo spesso.
Andrey

1
Se un indice è definito sulla accounts(account_customer)maggior parte degli RDBMS, utilizzerebbe quell'indice per scoprire esattamente quali righe del customersdatabase devono essere scansionate.
jemfinch

sì, ma comunque non è un'operazione economica. puoi memorizzare la somma in qualche campo e aggiornarla su ogni transazione.
Andrey

@jemfinch: no, non lo faranno. Ciò richiederebbe la scansione dell'intero indice solo per filtrare i clienti, quindi la scansione dell'indice del cliente in un ciclo annidato. A HASH JOINsarebbe molto più veloce, quindi è ciò che verrà utilizzato tranne in tutti i principali database tranne MySQL, che farà solo il passaggio customersin un ciclo annidato (poiché è di dimensioni inferiori)
Quassnoi

4

Joins are fast.I join dovrebbero essere considerati una pratica standard con uno schema di database correttamente normalizzato. I join ti consentono di unire diversi gruppi di dati in modo significativo. Non temere l'unione.

L'avvertenza è che è necessario comprendere la normalizzazione, l'unione e l'uso corretto degli indici.

Attenzione all'ottimizzazione prematura, poiché il fallimento numero uno di tutti i progetti di sviluppo è il rispetto della scadenza. Dopo aver completato il progetto e aver compreso i compromessi, puoi infrangere le regole se puoi giustificarlo.

È vero che le prestazioni del join si degradano in modo non lineare all'aumentare della dimensione del set di dati. Pertanto, non si ridimensiona così bene come le query su una singola tabella, ma è comunque scalabile.

È anche vero che un uccello vola più veloce senza ali, ma solo verso il basso.


3

I join richiedono un'elaborazione aggiuntiva poiché devono cercare in più file e più indici per "unire" i dati insieme. Tuttavia, "set di dati molto grandi" sono tutti relativi. Qual è la definizione di large? Nel caso dei JOIN, penso che sia un riferimento a un ampio set di risultati, non a quel set di dati complessivo.

La maggior parte dei database può elaborare molto rapidamente una query che seleziona 5 record da una tabella primaria e unisce 5 record da una tabella correlata per ogni record (supponendo che siano presenti gli indici corretti). Queste tabelle possono contenere centinaia di milioni di record ciascuna o addirittura miliardi.

Una volta che il set di risultati inizia a crescere, le cose rallenteranno. Utilizzando lo stesso esempio, se la tabella primaria restituisce 100.000 record, ci saranno 500.000 record "uniti" che devono essere trovati. Basta estrarre così tanti dati dal database con ulteriori ritardi.

Non evitare i JOIN, sappi solo che potresti dover ottimizzare / denormalizzare quando i set di dati diventano "molto grandi".


3

Anche dall'articolo che hai citato:

Molti siti Web su larga scala con miliardi di record, petabyte di dati, molte migliaia di utenti simultanei e milioni di query al giorno utilizzano uno schema di partizionamento orizzontale e alcuni sostengono addirittura la denormalizzazione come la migliore strategia per l'architettura del livello dati.

e

E a meno che tu non sia un sito molto grande, probabilmente non devi preoccuparti di questo livello di complessità.

e

È più soggetto a errori rispetto al fatto che il database faccia tutto questo lavoro, ma sei in grado di scalare oltre ciò che possono gestire anche i database di fascia più alta.

L'articolo parla di mega-siti come Ebay. A quel livello di utilizzo, probabilmente dovrai considerare qualcosa di diverso dalla semplice gestione dei database relazionali. Ma nel "normale" corso degli affari (applicazioni con migliaia di utenti e milioni di record) quegli approcci più costosi e più inclini all'errore sono eccessivi.


2

I join sono considerati una forza opposta alla scalabilità perché sono in genere il collo di bottiglia e non possono essere facilmente distribuiti o messi in parallelo.


Non sono sicuro che sia vero. So che Teradata è certamente in grado di distribuire join tra gli amplificatori. Ovviamente alcuni tipi di join possono essere più complicati / intrattabili di altri.
Cade Roux

gli indici possono essere partizionati in RDBMS che vanno da mysql a oracle. AFAIK che scala (è distribuito e può essere messo in parallelo).
Irragionevolezza

2

Tabelle progettate correttamente contenenti le indicazioni corrette e le query scritte correttamente non sempre lente. Dove mai hai sentito che:

Perché le giunzioni sono cattive o "lente"

non ha idea di cosa stiano parlando !!! La maggior parte dei join sarà molto veloce. Se devi unire molte righe contemporaneamente potresti prendere un colpo rispetto a una tabella denormalizzata, ma questo risale alle tabelle progettate correttamente, sappi quando denormalizzare e quando no. in un sistema di reporting pesante, suddividere i dati in tabelle denormalizzate per i report o persino creare un data warehouse. In un sistema transazionale pesante normalizzare le tabelle.


1

La quantità di dati temporanei generati potrebbe essere enorme in base ai join.

Ad esempio, un database qui al lavoro aveva una funzione di ricerca generica in cui tutti i campi erano opzionali. La routine di ricerca ha eseguito un join su ogni tabella prima dell'inizio della ricerca. All'inizio ha funzionato bene. Ma ora che la tabella principale ha oltre 10 milioni di righe ... non così tanto. Le ricerche ora richiedono 30 minuti o più.

Mi è stato assegnato il compito di ottimizzare la procedura di ricerca memorizzata.

La prima cosa che ho fatto è stata se si cercava uno qualsiasi dei campi della tabella principale, ho selezionato una tabella temporanea solo su quei campi. POI, ho unito tutte le tabelle con quella tabella temporanea prima di fare il resto della ricerca. Le ricerche in cui uno dei campi della tabella principale ora richiedono meno di 10 secondi.

Se nessuno dei campi della tabella principale viene avviato, eseguo ottimizzazioni simili per altre tabelle. Quando ho finito, nessuna ricerca richiede più di 30 secondi con la maggior parte sotto i 10.

Anche l'utilizzo della CPU del server SQL è andato di MODO GIÙ.


@BoltBait: il messaggio da portare via è che dovresti sempre provare a ridurre il numero di righe prima di eseguire un join?
unutbu

Sicuramente ha funzionato a meraviglia nel mio caso. Ma non ottimizzerei un sistema finché non diventa necessario.
BoltBait

normalmente non vengono generati dati temporanei sui join (dipende ovviamente dalla selettività, dalla memoria disponibile e dalla dimensione dei buffer dei join), AFAIK; tuttavia, i dati temporanei vengono generalmente creati su ordinazione e distinti se non esiste un indice che può essere utilizzato per tali operazioni.
Irragionevole

1

Mentre i join (presumibilmente a causa di un design normalizzato) possono ovviamente essere più lenti per il recupero dei dati rispetto a una lettura da una singola tabella, un database denormalizzato può essere lento per le operazioni di creazione / aggiornamento dei dati poiché l'impronta della transazione complessiva non sarà minima.

In un database normalizzato, un dato risiederà in un solo posto, quindi l'ingombro per un aggiornamento sarà il minimo possibile. In un database denormalizzato, è possibile che la stessa colonna in più righe o tra tabelle debba essere aggiornata, il che significa che l'impronta sarebbe maggiore e la possibilità di blocchi e deadlock può aumentare.


1

Ebbene, sì, selezionare le righe da una tabella denormalizzata (assumendo indici decenti per la tua query) potrebbe essere più veloce che selezionare le righe costruite dall'unione di più tabelle, in particolare se i join non hanno indici efficienti disponibili.

Gli esempi citati nell'articolo - Flickr e eBay - sono casi eccezionali IMO, quindi hanno (e meritano) risposte eccezionali. L'autore sottolinea specificamente la mancanza di RI e l'entità della duplicazione dei dati nell'articolo.

La maggior parte delle applicazioni - ancora una volta, IMO - beneficia della convalida e della ridotta duplicazione fornite dagli RDBMS.


0

Possono essere lenti se fatti in modo sciatto. Ad esempio, se fai un "seleziona *" su un join, probabilmente impiegherai un po 'di tempo per recuperare le cose. Tuttavia, se scegli attentamente quali colonne restituire da ciascuna tabella e con gli indici corretti in posizione, non dovrebbero esserci problemi.

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.