2010-06-02 12 views
14

Supponiamo di avere una tabella con circa 5 milioni di record e una colonna nvarchar(max) popolata con dati di testo di grandi dimensioni. Si desidera impostare questa colonna su NULL se SomeOtherColumn = 1 nel modo più rapido possibile.Il modo più veloce per eseguire l'aggiornamento di massa

La forza bruta UPDATE non funziona molto bene qui perché creerà una grande transazione implicita e richiederà sempre.

L'esecuzione di aggiornamenti in piccoli gruppi di record di 50 KB alla volta funziona, ma ci vogliono ancora 47 ore per essere completata su un server di 32 core/64 GB.

C'è un modo per fare questo aggiornamento più veloce? Ci sono suggerimenti di query magiche/opzioni di tabella che sacrifica qualcos'altro (come la concorrenza) in cambio di velocità?

NOTA: la creazione di una tabella temporanea o di una colonna temporanea non è un'opzione perché questa colonna nvarchar(max) comporta molti dati e pertanto occupa molto spazio!

PS: Sì, SomeOtherColumn è già indicizzato.

+0

Vedere anche: http://stackoverflow.com/questions/571750/make-sql-server-faster-at-manipulating-data-turn-off-transaction-logging –

+0

Come stai facendo il '50K batch records at un aggiornamento del tempo? È con una procedura memorizzata? Se è così, puoi inserire il codice? – Fede

+0

@ user356004: in rilettura non posso fare a meno di pensare che il tuo server sia sotto carico pesante o che non sia impostato correttamente: quei tempi sembrano molto alti. –

risposta

1

Hai provato a inserire un indice o statistiche su SomeOtherColumn?

+0

Se il problema di prestazioni è dovuto al fatto che non esiste un indice e quindi è necessaria una scansione della tabella per identificare le righe da aggiornare, non impiegherà più tempo (o più) per creare un nuovo indice e quindi emettere l'aggiornamento? –

+0

Davvero un indice su una colonna nvarchar (max)? – Paparazzi

3

È possibile impostare la modalità di ripristino del database su Simple per ridurre la registrazione, MA non farlo senza considerare le implicazioni complete per un ambiente di produzione.

Quali indici sono a posto sul tavolo? Dato che gli aggiornamenti batch di ca. 50.000 file impiegano così tanto tempo, direi che hai bisogno di un indice.

0

Provare a indicizzare 'SomeOtherColumn' ... I record 50K devono essere aggiornati in un attimo. Se è già presente un indice, vedere se l'indice deve essere riorganizzato e che le statistiche sono state raccolte per esso.

0

Se si sta eseguendo un ambiente di produzione con spazio insufficiente per duplicare tutti i tavoli, credo che si stiano cercando problemi prima o poi.

Se si forniscono alcune informazioni circa il numero di righe con SomeOtherColumn = 1, forse possiamo pensare in un altro modo, ma vi suggerisco:

0) Backup vostro tavolo 1) indice la bandiera della colonna 2) Set l'opzione tabella "senza log tranctions" ... se possibile 3) scrivere una stored procedure per eseguire gli aggiornamenti

+0

BTW ... hai intenzione di eseguire questa procedura più di una volta nella vita? –

+1

Come si imposta l'opzione tabella su "nessuna trancenza registro"? – user356004

3

Si spera che abbiate già eliminato tutti gli indici sulla colonna che state impostando su null, inclusi gli indici di testo completo. Come detto prima, disattivare temporaneamente le transazioni e il file di registro potrebbe fare il trucco. Il backup dei dati di solito troncerà anche i file di registro.

+0

Assicurati definitivamente di eliminare gli indici. Ha abbreviato le cose in modo significativo per me in passato. –

1

Questo mi ha davvero aiutato. Sono passato da 2 ore a 20 minuti con questo.

/* I'm using database recovery mode to Simple */ 
/* Update table statistics */ 

set transaction isolation level read uncommitted  

/* Your 50k update, just to have a measures of the time it will take */ 

set transaction isolation level READ COMMITTED 

Nella mia esperienza, lavorando in MSSQL 2005, lo spostamento di tutti i giorni (automaticamente) 4 Milioni 46 byte-Records (senza nvarchar (max) però) da una tabella in un database a un altro tavolo in un database diverso prende circa 20 minuti in un server QuadCore da 8 GB e 2 GHz e ciò non danneggia le prestazioni dell'applicazione. Spostando intendo INSERT INTO SELECT e quindi DELETE. L'utilizzo della CPU non supera mai il 30%, anche quando la tabella eliminata ha 28 milioni di record e fa costantemente circa 4K inserti al minuto, ma senza aggiornamenti. Bene, questo è il mio caso, può variare a seconda del carico del tuo server.

READ UNCOMMITTED

"Specifica che le dichiarazioni (aggiornamenti) in grado di leggere le righe che sono state modificate da altre transazioni ma non ancora impegnati." Nel mio caso, i record sono di sola lettura.

Non so cosa significhi rg-tsql ma here troverai informazioni sui livelli di isolamento della transazione in MSSQL.

+1

La "rg" è RedGate, una società sponsorizzata che fa pubblicità sui risultati per il tag [tsql]. – Corey

+1

Stai sempre attento e assicurati di comprendere le implicazioni della lettura delle transazioni non accettate. Sì, il tuo processo non dovrà aspettare le transazioni aperte da impegnare prima dell'eliminazione degli articoli, ma ovviamente se la transazione non viene confermata dopo tutto ciò significherebbe che hai cancellato la riga in modo errato !! – Cobusve

7

Da tutto ciò che riesco a vedere non sembra che i problemi siano legati agli indici.

La chiave sembra essere nel fatto che il campo nvarchar (max) contiene "lotti" di dati. Pensa a ciò che SQL deve fare per eseguire questo aggiornamento.

Poiché la colonna che si sta aggiornando è probabilmente più di 8000 caratteri è memorizzata fuori pagina, il che implica uno sforzo ulteriore nella lettura di questa colonna quando non è NULL.

Quando si esegue un batch di aggiornamenti 50000, SQL deve inserirlo in una transazione implicita per consentire il ripristino in caso di problemi. Per eseguire il rollback, è necessario memorizzare il valore originale della colonna nel log delle transazioni.

Assumendo (per semplicità) che ogni colonna contenga in media 10.000 byte di dati, ciò significa che 50.000 righe conterranno circa 500 MB di dati, che devono essere memorizzati temporaneamente (in modalità di ripristino semplice) o permanentemente (in completo ripristino modalità).

Non esiste alcun modo per disabilitare i registri poiché comprometterebbe l'integrità del database.

Ho eseguito un test rapido sul desktop lento del mio cane e l'esecuzione di batch pari a 10.000 diviene proibitivamente lenta, ma ridurre le dimensioni a 1000 righe, che implica una dimensione di registro temporanea di circa 10 MB, ha funzionato perfettamente.

Ho caricato un tavolo con 350.000 righe e ne ho contrassegnato 50.000 per l'aggiornamento. Questa operazione è stata completata in circa 4 minuti e, poiché è scalabile in modo lineare, dovresti essere in grado di aggiornare le tue 5 milioni di righe sul desktop lento del mio cane in circa 6 ore sul mio desktop da 2 GB da 1 processore, quindi mi aspetto qualcosa di molto meglio sul server rinforzato. da SAN o qualcosa del genere.

È possibile eseguire l'istruzione di aggiornamento come selezione, selezionando solo la chiave primaria e la grande colonna nvarchar e assicurarsi che venga eseguita alla velocità desiderata.

Ovviamente il collo di bottiglia potrebbero essere altri utenti che bloccano cose o contese sulla memoria o sul server, ma dato che non hai menzionato altri utenti, suppongo tu abbia il DB in modalità utente singolo per questo.

Come ottimizzazione, è necessario assicurarsi che i registri delle transazioni si trovino su un diverso disco fisico/gruppo di dischi rispetto ai dati per ridurre al minimo i tempi di ricerca.

Problemi correlati