Wie man SQL einfügt und / oder aktualisiert, um nicht die gesamte Tabelle auf MS SQL Server zu sperren

13

Sehr viel ein Neuling auf DB-Arbeit, also schätzen Sie Ihre Geduld mit einer grundlegenden Frage. Ich führe SQL Server 2014 auf meinem lokalen Computer aus und habe eine kleine Tabelle und eine grundlegende Clientanwendung, mit der ich verschiedene Ansätze testen kann. Ich erhalte , was scheint , während sowohl eine Tabellensperre zu sein INSERT INTOund UPDATEAnweisungen. Der Client ist eine ASP.NET-Anwendung mit dem folgenden Code:

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();

Ich führe diesen Code aus, dann aus dem Management Studio, das ich ausführe SELECT * FROM LAYOUTSv2. In beiden Fällen, in denen der Client-Thread angehalten wurde (dh vor dem Festschreiben / Zurücksetzen), bleibt die SELECT-Abfrage hängen, bis das Festschreiben / Zurücksetzen erfolgt.

Der Tabelle ist das Feld LAYOUTS_key als Primärschlüssel zugewiesen. Im Eigenschaftenfenster wird angezeigt, dass es eindeutig und gruppiert ist, wobei sowohl Seitensperren als auch Zeilensperren zulässig sind. Die Sperren-Eskalationseinstellung für die Tabelle ist Deaktivieren ... Ich habe beide verfügbaren Einstellungen von Tabelle und AUTO ohne Änderungen ausprobiert. Ich habe es versucht SELECT ... WITH (NOLOCK)und das gibt sofort ein Ergebnis zurück, aber wie hier und an anderen Orten gut gewarnt wird, ist es nicht das, was ich tun sollte. Ich habe versucht, den ROWLOCKHinweis auf die INSERTund UPDATE-Anweisungen zu setzen, aber nichts hat sich geändert.

Das Verhalten, nach dem ich suche, ist das Folgende: Vor dem Festschreiben von INSERTlesen Abfragen von anderen Threads alle Zeilen mit Ausnahme derjenigen, die INSERTbearbeitet werden. UPDATELesen Sie vor dem Festschreiben von Abfragen aus anderen Threads die Startversion der zu bearbeitenden Zeile UPDATE. Kann ich das auf irgendeine Weise tun? Wenn ich weitere Informationen zur Klärung meines Anwendungsfalls benötige, lassen Sie es mich bitte wissen. Vielen Dank.

John Riehl
quelle
3
Übrigens WHERE LAYOUTS_key='" + newkey + "'ist ein komplettes Nein-Nein aus verschiedenen Gründen, einschließlich SQL-Injection, sollten Sie parametrisierte Abfragen verwenden.
Martin Smith
1
@MartinSmith Vielen Dank für das Heads-up zu diesem Thema. Ich habe weder von parametrisierten Abfragen noch von SQL-Injection-Angriffen gehört.
John Riehl
@JohnRiehl, re: Injection-Attacken, stell dir vor, dein User setzt newkeyauf " something';DELETE FROM LAYOUTSv2 --". Ihre Aktualisierung wurde erfolgreich abgeschlossen und die Tabelle anschließend geleert, da der Benutzer die Abfrage durch Einfügen eines Apostrophs manipuliert hat. Normalerweise sieht eine parametrisierte Abfrage wie folgt aus: Danach UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?weisen Sie dem ?(dem Parameter) in Ihrem Code separat Werte zu .
Daniel Hutmacher

Antworten:

9

Wahrscheinlich wird nicht der gesamte Tisch gesperrt.

Es wird eine Zeile in der Tabelle SELECT * FROM LAYOUTSv2gesperrt, aber Sie versuchen, die gesamte Tabelle zu lesen, sodass diese Sperre zwangsläufig blockiert wird.

Für das Einfügen von Groß- und Kleinschreibung können Sie lediglich den READPASTHinweis zum Überspringen der gesperrten UPDATEZeile angeben. Dies ergibt jedoch nicht das gewünschte Ergebnis für die Groß- und Kleinschreibung (es wird die Zeile erneut übersprungen und nicht die Startversion der Zeile gelesen).

Wenn Sie die Datenbank für die Read Committed Snapshot-Isolation konfigurieren , erhalten Sie in beiden Fällen den gewünschten Effekt (auf Kosten einer stärkeren Verwendung von tempdb).

Martin Smith
quelle
Ich habe "Ist Read Committed Snapshot On" in "True" geändert und jetzt funktioniert es einwandfrei, ohne dass Hinweise benötigt werden. Vielen Dank! Ein Follow-up ... Ich habe "Allow Snapshot Isolation" auf "False" gesetzt ... ist das in Ordnung? Vielen Dank.
John Riehl
@JohnRiehl - Ja, wenn Sie die SNAPSHOTIsolation nicht explizit verwenden , lassen Sie sie am besten deaktiviert und aktivieren Sie sie dann, wenn Sie sich später dazu entschließen, dass dies für Sie nützlich ist.
Martin Smith
6

Mit den Anweisungen insert und update sollen Sperren auf Zeilenebene erstellt werden. Wenn die Anzahl der Sperren in einer Transaktion jedoch 5.000 oder mehr beträgt, tritt eine Sperreneskalation auf und es wird eine Sperre auf Tabellenebene erstellt. Siehe unten.

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

Suraj
quelle