2009-05-20 3 views
5

Ho una tabella in un database di SQL Server 2005 che viene utilizzato molto. Ha le nostre informazioni sulla disponibilità del prodotto a portata di mano. Riceviamo aggiornamenti ogni ora dal nostro magazzino e negli ultimi anni abbiamo eseguito una routine che tronca la tabella e aggiorna le informazioni. Questo richiede solo pochi secondi e non è stato un problema, fino ad ora. Abbiamo molte più persone che utilizzano i nostri sistemi che interrogano queste informazioni ora, e di conseguenza stiamo vedendo un sacco di timeout a causa dei processi di blocco.Qual è il modo migliore per aggiornare i dati in una tabella mentre è in uso senza bloccare la tabella?

... così ...

abbiamo ricercato le nostre opzioni e sono venuti con l'idea di mitigare il problema.

  1. Avremmo due tabelle. Tabella A (attiva) e tabella B (non attiva).
  2. Creiamo una vista che punta alla tabella attiva (tabella A).
  3. Tutte le informazioni che necessitano di questa tabella (4 oggetti) ora dovrebbero passare attraverso la vista.
  4. La routine oraria troncarebbe la tabella inattiva, aggiornarla con le informazioni più recenti quindi aggiornare la vista in modo da puntare alla tabella inattiva, rendendola attiva.
  5. Questa routine determinerebbe quale tabella è attiva e in pratica cambia la visualizzazione tra di loro.

Cosa c'è di sbagliato in questo? Cambiare la visualizzazione della query intermedia causerà problemi? Questo può funzionare?

Grazie per la vostra esperienza.

Informazioni Extra

  • la routine è un pacchetto SSIS che peforms molti passi e alla fine tronca/aggiorna la tabella in questione

  • I processi di blocco sono altri due stored procedure che interrogano questo tavolo.

+0

se si dispone delle licenze, due server con bilanciamento del carico separato potrebbe fornire un'alternativa senza soluzione di continuità. Ne mantieni uno in diretta e aggiorni l'altro e poi cambi. –

risposta

6

Avete considerato l'utilizzo di snapshot isolation. Ti permetterebbe di iniziare una grossa transazione per il tuo SSIS e continuare a leggere dal tavolo.

Questa soluzione sembra molto più pulita rispetto al passaggio dei tavoli.

+0

+1 su questo. È esattamente quello che dovresti fare. Idealmente, riceverai aggiornamenti che non richiedono il dumping di tutto, ma dal momento che non lo fai, il trucco è avviare una transazione, eliminare (non troncare) tutto, quindi caricare tutto. Nel momento in cui esegui il commit, il database farà cambiare tutto senza lasciare spazi. –

+0

Perché non troncare? Non sarebbe più lento da cancellare poiché ogni cancellazione è registrata? –

+0

Penso che sia questo il punto. usando l'isolamento dell'istantanea, è il commit che induce gli utenti a vedere i nuovi dati. truncate non è registrato e non so come potrebbe influenzare il transacton ma probabilmente non come vorresti. In effetti scommetto che il truncate fallirebbe se la tabella avesse una transazione in sospeso. – Zack

2

penso che questo sta andando su di esso nel modo sbagliato - aggiornamento di una tabella deve bloccarlo, anche se è possibile limitare tale blocco per per pagina o addirittura per riga.

Guarderei non troncare il tavolo e riempirlo. Ciò interferirà sempre con gli utenti che cercano di leggerlo.

Se si aggiorna invece di sostituire la tabella, è possibile controllare l'altro modo: gli utenti che leggono non dovrebbero bloccare la tabella e potrebbero essere in grado di ottenere letture ottimistiche.

Provare ad aggiungere il suggerimento with (nolock) all'istruzione SQL View di lettura. Dovresti essere in grado di ottenere grandi volumi di lettura da parte degli utenti anche se la tabella viene regolarmente aggiornata.

+0

È a mia conoscenza che SQl Hints, in particolare NOLOCK, sono negativi per molte ragioni. http://tinyurl.com/qwloxh –

+1

Non sono neanche lontanamente "cattivi" come la creazione di copie dei dati e la ricostruzione di viste al fine di evitare il blocco. Il modo migliore è quello di impostare il blocco è in realtà nelle proprietà della transazione, ma non è possibile qui perché stai usando troncato. Inoltre non sono d'accordo con i punti formulati in quell'articolo - a volte gli hint SQL sono il modo migliore per gestire i deadlock, ma devi capire cosa stai facendo con loro. – Keith

+0

Oh, e SO lo fa: http://www.codinghorror.com/blog/archives/001166.html – Keith

1

Perché non utilizzare le transazioni per aggiornare le informazioni anziché un'operazione di troncamento.

Truncate non è registrato, pertanto non può essere eseguito in una transazione.

Se l'operazione viene eseguita in una transazione, gli utenti esistenti non saranno interessati.

Il modo in cui questo viene eseguito dipenderà da cose come la dimensione della tabella e quanto radicalmente i dati cambiano. Se dai maggiori dettagli forse potrei consigliarti ulteriormente.

+0

La routine è un pacchetto SSIS e sembra che il tutto sia in esecuzione in una transazione, che è ciò che causa il blocco. –

2

Personalmente, se si sta sempre introducendo tempi di inattività per eseguire un processo batch rispetto al tavolo, penso che si dovrebbe gestire l'esperienza dell'utente nel livello di accesso business/dati. Introdurre un oggetto di gestione tabella che monitora le connessioni a tale tabella e controlla l'elaborazione batch.

Quando i nuovi dati batch sono pronti, l'oggetto di gestione interrompe tutte le nuove richieste di query (forse anche in coda? L'oggetto di gestione può generare un evento (BatchProcessingEvent) che il livello dell'interfaccia utente può interpretare per far sapere alla gente che la tabella non è attualmente disponibile.

mio $ 0,02

Nate

+0

Abbiamo preso in considerazione questo e ci piace molto questa opzione, ma richiede molto più lavoro per andare avanti, e il tempo è un lusso che purtroppo non abbiamo. Ma sono d'accordo nel gestire questo indipendentemente dal fatto che il processo batch del server sia ottimale. –

0

Lo facciamo sui nostri sistemi ad alta utilizzo e non abbiamo avuto alcun problema. Tuttavia, come per tutti i database di cose, l'unico modo per essere sicuri che sarebbe di aiuto sarebbe quello di fare la modifica in dev e quindi caricarlo. Non sapendo che altro fa il tuo pacchetto SSIS, potrebbe comunque causare blocchi.

1

Una possibile soluzione sarebbe quella di ridurre al minimo il tempo necessario per aggiornare la tabella.

Vorrei prima creare una tabella di staging per scaricare i dati dal magazzino.

Se avete a che fare "inserimenti, aggiornamenti ed eliminazioni" nella tabella finale

Consente supponiamo che il tavolo finale si presenta così:

Table Products: 
    ProductId  int 
    QuantityOnHand Int 

ed è necessario aggiornare QuantityOnHand dal magazzino.

prima creare una tabella di gestione temporanea come:

Table Prodcuts_WareHouse 
    ProductId  int 
    QuantityOnHand Int 

e quindi creare una tabella "Azioni" come questo:

Table Prodcuts_Actions 
    ProductId  int 
    QuantityOnHand Int 
    Action   Char(1) 

Il processo di aggiornamento dovrebbe quindi essere qualcosa di simile:

1.Truncate table Prodcuts_WareHouse

2.Truncate table Prodcuts_Actions

3.Fill tabella Prodcuts_WareHouse con i dati dal magazzino

4.Riempire la tabella di Prodcuts_Actions con questo:

Inserti:

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  SRC.ProductId, SRC.QuantityOnHand, 'I' AS ACTION 
FROM   Prodcuts_WareHouse AS SRC LEFT OUTER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId 
WHERE  (DEST.ProductId IS NULL) 

Elimina

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  DEST.ProductId, DEST.QuantityOnHand, 'D' AS Action 
FROM   Prodcuts_WareHouse AS SRC RIGHT OUTER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId 
WHERE  (SRC.ProductId IS NULL) 

Aggiornamenti

INSERT INTO Prodcuts_Actions (ProductId, QuantityOnHand,Action) 
SELECT  SRC.ProductId, SRC.QuantityOnHand, 'U' AS Action 
FROM   Prodcuts_WareHouse AS SRC INNER JOIN 
         Products AS DEST ON SRC.ProductId = DEST.ProductId AND SRC.QuantityOnHand <> DEST.QuantityOnHand 

Fino ad ora non si è bloccato il tavolo finale.

5.In una transazione di aggiornamento del tavolo finale:

BEGIN TRANS 

DELETE Products FROM Products INNER JOIN 
Prodcuts_Actions ON Products.ProductId = Prodcuts_Actions.ProductId 
WHERE  (Prodcuts_Actions.Action = 'D') 

INSERT INTO Prodcuts (ProductId, QuantityOnHand) 
SELECT ProductId, QuantityOnHand FROM Prodcuts_Actions WHERE Action ='I'; 

UPDATE Products SET QuantityOnHand = SRC.QuantityOnHand 
FROM   Products INNER JOIN 
Prodcuts_Actions AS SRC ON Products.ProductId = SRC.ProductId 
WHERE  (SRC.Action = 'U') 

COMMIT TRAN 

Con tutto il processo di cui sopra, si minimizza la quantità di record da aggiornare al minimo necessario, e quindi il tempo al tavolo finale sarà bloccato durante l'aggiornamento.

È anche possibile non utilizzare una transazione nel passaggio finale, quindi tra comandi verrà rilasciata la tabella.

1

Se si dispone di Enterprise Edition di SQL Server, è possibile suggerire di utilizzare la tecnologia di partizionamento di SQL Server.

È possibile che i dati attualmente richiesti si trovino nella partizione "In diretta" e nella versione aggiornata dei dati nella partizione "Secondaria" (che non è disponibile per le query ma per l'amministrazione dei dati).

Una volta che i dati sono stati importati nella parzializzazione "Secondaria", è possibile CAMBIARE istantaneamente la partizione "LIVE" OUT e la partizione "Secondaria" IN, con conseguente zero tempi di inattività e nessun blocco.

Una volta effettuato lo switch, è possibile andare a troncare i dati non più necessari senza che gli utenti colpiscano gli utenti dei dati appena pubblicati (in precedenza la partizione secondaria).

Ogni volta che è necessario eseguire un processo di importazione, è sufficiente ripetere/invertire la procedura.

Per saperne di più su SQL Server Partitioning vedere:

http://msdn.microsoft.com/en-us/library/ms345146(SQL.90).aspx

Oppure si può basta chiedere a me :-)

EDIT:

Su un lato nota, al fine per risolvere eventuali problemi di blocco, è possibile utilizzare la tecnologia di Versioning delle righe di SQL Server.

http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

+0

Questa è l'idea di ciò che volevamo ottenere con una vista, ma abbiamo solo l'edizione standard di SQL Server. –

2

appena letto si utilizza SSIS

è possibile utilizzare il componente TableDiference da: http://www.sqlbi.eu/Home/tabid/36/ctl/Details/mid/374/ItemID/0/Default.aspx

alt text http://www.sqlbi.eu/Portals/0/Articles/Table%20Difference%20Images/DataFlowSimple.png

In questo modo è possibile applicare le modifiche alla tabella, ONE by Uno, ma ovviamente, sarà molto più lento e, a seconda delle dimensioni della tabella, richiederà più RAM sul server, ma il problema di blocco verrà completamente corretto.

0

Se la tabella non è molto grande, è possibile memorizzare i dati nell'applicazione per un breve periodo di tempo. Potrebbe non eliminare del tutto il blocco, ma ridurrebbe le possibilità che la tabella venga interrogata quando si verifica un aggiornamento.

0

Forse avrebbe senso fare un'analisi dei processi che stanno bloccando poiché sembrano essere la parte del tuo paesaggio che è cambiata. Ci vuole solo una query scritta male per creare i blocchi che stai vedendo. Escludendo una query scritta male, forse la tabella ha bisogno di uno o più indici di copertura per accelerare le query e tornare indietro sulla tua strada senza dover riprogettare il tuo codice già funzionante.

Spero che questo aiuti,

Bill