Come ottenere l'inserimento e / o l'aggiornamento di SQL per non bloccare l'intera tabella su MS SQL Server


13

Molto un principiante nel lavoro su DB, quindi apprezza la tua pazienza con una domanda di base. Sto eseguendo SQL Server 2014 sul mio computer locale e ho una piccola tabella e un'applicazione client di base con cui testare diversi approcci. Ricevo quello che sembra essere un blocco della tabella durante entrambe INSERT INTOe le UPDATEdichiarazioni. Il client è un'applicazione ASP.NET con il seguente codice:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Eseguo questo codice, quindi dallo studio di gestione corro SELECT * FROM LAYOUTSv2. In entrambi i casi in cui il thread client è in pausa (ovvero prima del commit / rollback) la query SELECT si blocca fino a quando si verifica il commit / rollback.

La tabella ha il campo LAYOUTS_key assegnato come chiave primaria. Nella finestra delle proprietà mostra che è univoco e raggruppato, con blocchi di pagina e blocchi di riga consentiti entrambi. L'impostazione dell'escalation del blocco per la tabella è Disabilita ... Ho provato entrambe le altre impostazioni disponibili di Tabella e AUTO senza modifiche. Ho provato SELECT ... WITH (NOLOCK)e questo restituisce immediatamente un risultato, ma come è ben avvertito qui e in altri luoghi non è quello che dovrei fare. Ho provato a dare il ROWLOCKsuggerimento su entrambe le dichiarazioni INSERTe UPDATE, ma nulla è cambiato.

Il comportamento che sto cercando è questo: prima di eseguire il commit di un INSERT, le query di altri thread leggono tutte le righe tranne quella che viene modificata INSERT. Prima di eseguire il commit di una UPDATEquery da altri thread leggere la versione iniziale della riga in fase di modifica UPDATE. C'è un modo per farlo? Se devo fornire altre informazioni per chiarire il mio caso d'uso, per favore fatemelo sapere. Grazie.


3
A proposito WHERE LAYOUTS_key='" + newkey + "'è un no-no completo per vari motivi tra cui l'iniezione SQL, è necessario utilizzare query con parametri.
Martin Smith,

1
@MartinSmith Grazie per l'heads-up su questo ... mai sentito parlare di query con parametri o attacchi SQL Injection.
John Riehl,

@JohnRiehl, re: attacchi di iniezione, immagina se il tuo utente si imposta newkeysu " something';DELETE FROM LAYOUTSv2 --". L'aggiornamento verrà completato correttamente e quindi svuotare la tabella perché l'utente ha manipolato la query inserendo un apostrofo. Normalmente, una query con parametri ha un aspetto simile UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, dopo di che si assegnano separatamente i valori al ?(parametro) nel codice.
Daniel Hutmacher,

Risposte:


10

È probabile che non stia bloccando "l'intera tabella".

Sta bloccando una riga nella tabella ma i tuoi SELECT * FROM LAYOUTSv2tentativi di leggere l'intera tabella sono necessariamente bloccati da quel blocco.

Per il caso di inserimento puoi semplicemente specificare il READPASTsuggerimento per saltare la riga bloccata, tuttavia ciò non fornirà il risultato desiderato per il UPDATEcaso (salterà di nuovo la riga non leggendo la versione iniziale della riga).

Se si configura il database per l' isolamento dell'istantanea con commit in lettura, ciò darà l'effetto desiderato per entrambi i casi (a spese di un maggiore utilizzo di tempdb)


Ho cambiato "Is Read Committed Snapshot On" su True e ora funziona perfettamente senza suggerimenti. Grazie! Un follow-up ... Ho lasciato "Consenti isolamento snapshot" impostato su False ... va bene? Grazie.
John Riehl,

@JohnRiehl - Sì, se non stai usando in modo esplicito l' SNAPSHOTisolamento migliore per lasciarlo disabilitato e quindi abilitarlo se successivamente decidi che sarebbe utile per te.
Martin Smith,

7

Le istruzioni insert e update dovrebbero creare blocchi a livello di riga. Tuttavia, quando il numero di blocchi in una transazione è pari o superiore a 5.000, si verifica un'escalation del blocco e viene creato un blocco a livello di tabella. Vedi sotto.

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx


Non pertinente a questa domanda poiché entrambe le istruzioni INSERT e UPDATE stanno scrivendo una singola riga
Martin Smith,
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.