Cosa fa specificamente OracleBulkCopy e come posso ottimizzarne le prestazioni?


14

Riassumendo le specifiche: è necessario inserire circa 5 milioni di righe in un database del fornitore (Oracle). Tutto va benissimo per i batch di 500k righe utilizzando OracleBulkCopy(ODP.NET), ma quando proviamo a scalare fino a 5M, le prestazioni iniziano a rallentare a una scansione quando raggiunge il segno 1M, diventano progressivamente più lente man mano che vengono caricate più righe e, infine, timeout dopo circa 3 ore.

Ho il sospetto che sia correlato a una chiave primaria sul tavolo, ma ho esplorato i forum Oracle e Stack Overflow per informazioni e molto di ciò che sto leggendo contraddice che (anche molti post sembrano contraddirsi a vicenda ) . Spero che qualcuno possa mettere le cose in chiaro su alcune domande strettamente correlate sul processo:

  1. La OracleBulkCopyclasse utilizza il caricamento convenzionale o diretto? C'è un modo per confermare questo, in un modo o nell'altro?

  2. Supponendo che fa diretta-path uso di carico: E 'vero che Oracle imposta automaticamente tutti gli indici a inutilizzabili durante il carico e li mette di nuovo online dopo? Ho letto diverse dichiarazioni in tal senso ma, ancora una volta, non posso confermarlo.

  3. Se il numero 2 è vero, allora dovrebbe fare la differenza quali indici sono sul tavolo prima di iniziare un'operazione di copia in blocco? Se è così, perché?

  4. Relativamente al n. 3, c'è qualche differenza pratica, in generale, tra il caricamento di massa con un indice inutilizzabile rispetto al fatto di far cadere l'indice prima del carico e ricrearlo in seguito?

  5. Se il n. 2 non è corretto o se ci sono alcuni avvertimenti che non capisco, farebbe qualche differenza rendere esplicitamente inutilizzabile l'indice prima del caricamento di massa, e poi ricostruirlo esplicitamente in seguito?

  6. C'è qualcos'altro, oltre alle build dell'indice, che potrebbe far rallentare progressivamente un'operazione di copia in blocco man mano che vengono aggiunti sempre più record? (Forse qualcosa a che fare con la registrazione, anche se mi aspetterei che le operazioni di massa non vengano registrate?)

  7. Se in realtà non c'è altro modo per ottenere le prestazioni da snuff oltre a eliminare prima il PK / indice, quali passi posso fare per assicurarmi che l'indice non scompaia completamente, cioè se si perde la connessione al database nel mezzo del processo?


Nota a margine: i dati da copiare sono già ordinati in base al PK, che è l'unico indice sulla tabella.
Aaronaught,

Stai usando un DataReader per leggere i dati dalla fonte?
bernd_k,

@bernd_k: No, caricamento interamente dalla memoria. Non è sicuramente la fonte il problema.
Aaronaught,

Risposte:


13

Ancora qualche giorno di lettura e sperimentazione e sono stato in grado (principalmente) di rispondere a molti di questi:

  1. Ho trovato questo sepolto nella documentazione ODP.NET (ironicamente non nei OracleBulkCopydocumenti):

    La funzionalità di copia bulk ODP.NET utilizza un approccio di caricamento diretto del percorso, simile a Oracle SQL * Loader, ma non uguale. L'uso del caricamento diretto del percorso è più rapido del caricamento convenzionale (utilizzando INSERTistruzioni SQL convenzionali ).

    Così sembra che non usa percorso diretto.

  2. Questo sono stato in grado di verificare eseguendo un'operazione di copia di grandi dimensioni e ottenendo le proprietà dell'indice da SQL Developer. L'indice ha appaiono come UNUSABLEmentre la copia di massa era in corso. Tuttavia , ho anche scoperto che OracleBulkCopy.WriteToServersi rifiuterà di funzionare se l'indice inizia in uno UNUSABLEstato, quindi chiaramente c'è di più qui, perché se fosse semplice come disabilitare e ricostruire l'indice, non dovrebbe interessarsi dello stato iniziale.

  3. Fa la differenza specificamente se l'indice è anche un vincolo . Ho trovato questo piccolo gioiello nella documentazione collegata sopra:

    Vincoli abilitati
    Durante una copia bulk Oracle, i seguenti vincoli sono automaticamente abilitati per impostazione predefinita:

    • NOT NULL
    • UNIQUE
    • PRIMARY KEY (vincoli univoci su colonne non nulle)

    NOT NULLi vincoli vengono controllati al momento della creazione dell'array di colonne. Qualsiasi riga che viola il NOT NULLvincolo viene rifiutata.

    UNIQUEi vincoli vengono verificati quando gli indici vengono ricostruiti alla fine del carico. L'indice viene lasciato in uno stato dell'indice inutilizzabile se viola un UNIQUEvincolo.

    La documentazione è un po 'confusa su ciò che accade durante il caricamento, specialmente con le chiavi primarie, ma una cosa è assolutamente certa: si comporta diversamente con una chiave primaria rispetto a una senza . Dal momento che OracleBulkCopyti consentirà felicemente di violare i vincoli dell'indice (e di puntare l'indice allo UNUSABLEstato quando è fatto), il mio sospetto è che sta costruendo l'indice PK durante la copia di massa ma semplicemente no convalida fino a dopo.

  4. Non sono sicuro se la differenza osservata sia all'interno di Oracle stesso o solo una stranezza di OracleBulkCopy. La giuria è ancora fuori per questo.

  5. OracleBulkCopygenererà un'eccezione se un indice è inizialmente nello UNUSABLEstato, quindi è davvero un punto controverso.

  6. Se ci sono altri fattori, gli indici (e in particolare gli indici PK) sono ancora i più importanti, come ho scoperto da:

  7. Creare una tabella temporanea globale con lo stesso schema (usando CREATE AS), quindi copiare in blocco nella tabella temporanea e infine fare un vecchio semplice INSERTdalla tabella temporanea alla tabella reale. Dal momento che la tabella temporanea non ha indice, la copia in blocco avviene molto velocemente e anche il finale INSERTè veloce perché i dati sono già in una tabella (non ho ancora provato il suggerimento di aggiunta, poiché una copia da tabella a riga da 5 M richiede già meno di 1 minuto).

    Non sono ancora sicuro delle potenziali ramificazioni di (ab) utilizzando il tablespace temporaneo in questo modo, ma finora non mi ha dato alcun problema ed è molto più sicuro dell'alternativa per prevenire la corruzione di entrambe le righe o indici.

    Il successo di questo dimostra anche abbastanza chiaramente che l'indice PK è il problema, poiché questa è l'unica differenza pratica tra la tabella temporanea e la tabella permanente, entrambe avviate con zero righe durante i test delle prestazioni.

Conclusione: non preoccuparti di provare a copiare in blocco più di circa 100.000 righe in una tabella Oracle indicizzata utilizzando ODP.NET. Rilasciare l'indice (se non è effettivamente necessario) o "precaricare" i dati in una tabella diversa (non indicizzata).


Non sono sicuro del controllo dei vincoli della chiave primaria. Sono felice di aver inserito in blocco gli stessi dati in una tabella Oracle 2 volte e Select * mostra 2 righe duplicate. In quello stato Elimina non è possibile, ma troncare la tabella aiuta a tornare a uno stato pulito.
bernd_k,

@bernd_k: Deleteimpossibile perché l'indice lo è UNUSABLE. Questo è il risultato del controllo dei vincoli che si verifica alla fine della copia bulk.
Aaronaught,

Ho uno script PowerShell in esecuzione, che chiama bulkcopy in un database Oracle da un lettore di dati SQL Server, tutte le tabelle di destinazione con chiavi primarie e non ho problemi con le tabelle con un massimo di 205278 righe. Ma sto molto attento a riempire prima le tabelle principali prima di riempire le tabelle dei dettagli. Non ho rimosso nessuno degli altri indici sulla tabella e non ho problemi quando le tabelle sono inizialmente vuote.
bernd_k,

@bernd_k: Sì, non ho avuto troppi problemi nemmeno a quel volume (vedi il mio ultimo paragrafo). È quando raggiungi i milioni che diventa orribile. Inoltre, potrebbe esserci una differenza se svuoti la tabella dopo ogni copia in blocco (questa non viene svuotata, viene aggiunta e sai come gli indici diventano più lenti man mano che diventano più grandi).
Aaronaught,

Forse aiuta quando fai unalter session set skip_unusable_indexes = true;
Wernfried Domscheit il

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.