Ich verwende eine Microsoft SQL Server 2005-Datenbank mit Isolationsstufe READ_COMMITTED
und READ_COMMITTED_SNAPSHOT=ON
.
Jetzt möchte ich verwenden:
SELECT * FROM <tablename> FOR UPDATE
... damit andere Datenbankverbindungen blockieren, wenn versucht wird, auf dieselbe Zeile "FOR UPDATE" zuzugreifen.
Ich habe es versucht:
SELECT * FROM <tablename> WITH (updlock) WHERE id=1
... aber dies blockiert alle anderen Verbindungen, auch wenn eine andere ID als "1" ausgewählt wird.
Welches ist der richtige Hinweis SELECT FOR UPDATE
für Oracle, DB2, MySql?
EDIT 2009-10-03:
Dies sind die Anweisungen zum Erstellen der Tabelle und des Index:
CREATE TABLE example ( Id BIGINT NOT NULL, TransactionId BIGINT,
Terminal BIGINT, Status SMALLINT );
ALTER TABLE example ADD CONSTRAINT index108 PRIMARY KEY ( Id )
CREATE INDEX I108_FkTerminal ON example ( Terminal )
CREATE INDEX I108_Key ON example ( TransactionId )
Viele parallele Prozesse tun dies SELECT
:
SELECT * FROM example o WITH (updlock) WHERE o.TransactionId = ?
EDIT 2009-10-05:
Zur besseren Übersicht habe ich alle erprobten Lösungen in der folgenden Tabelle aufgeführt:
Mechanismus | SELECT für verschiedene Zeilenblöcke | SELECT für dieselben Zeilenblöcke ----------------------- + -------------------------- ------ + -------------------------- ROWLOCK | nein | Nein Updlock, Rowlock | ja | Ja xlock, rowlock | ja | Ja repeatableread | nein | Nein DBCC TRACEON (1211, -1) | ja | Ja Rowlock, Xlock, Holdlock | ja | Ja Updlock, Holdlock | ja | Ja UPDLOCK, READPAST | nein | Nein Ich suche | nein | Ja
Antworten:
Vor kurzem hatte ich ein Deadlock-Problem, weil SQL Server mehr als nötig sperrt (Seite). Man kann nichts dagegen tun. Jetzt fangen wir Deadlock-Ausnahmen ein ... und ich wünschte, ich hätte stattdessen Oracle.
Bearbeiten: Wir verwenden mittlerweile die Snapshot-Isolation, die viele, aber nicht alle Probleme löst. Um die Snapshot-Isolation verwenden zu können, muss sie leider vom Datenbankserver zugelassen werden, was zu unnötigen Problemen beim Kunden führen kann. Jetzt fangen wir nicht nur Deadlock-Ausnahmen ab (die natürlich immer noch auftreten können), sondern auch Snapshot-Parallelitätsprobleme, um Transaktionen aus Hintergrundprozessen zu wiederholen (die vom Benutzer nicht wiederholt werden können). Dies ist jedoch immer noch viel besser als zuvor.
quelle
Ich habe ein ähnliches Problem, ich möchte nur 1 Zeile sperren. Soweit ich weiß,
UPDLOCK
sperrt SQLSERVER mit der Option alle Zeilen, die gelesen werden müssen, um die Zeile abzurufen. Wenn Sie also keinen Index für den direkten Zugriff auf die Zeile definieren, werden alle vorhergehenden Zeilen gesperrt. In Ihrem Beispiel:Angenommen, Sie haben eine Tabelle mit dem Namen TBL mit einem
id
Feld. Sie möchten die Zeile mit sperrenid=10
. Sie müssen einen Index für die Feld-ID definieren (oder für alle anderen Felder, die an Ihrer Auswahl beteiligt sind):CREATE INDEX TBLINDEX ON TBL ( id )
Und dann lautet Ihre Abfrage, NUR die von Ihnen gelesenen Zeilen zu sperren:
SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10.
Wenn Sie die Option INDEX (TBLINDEX) nicht verwenden, muss SQLSERVER alle Zeilen vom Anfang der Tabelle lesen, um Ihre Zeile mit zu finden
id=10
, damit diese Zeilen gesperrt werden.quelle
Sie können nicht gleichzeitig eine Snapshot-Isolierung durchführen und Lesevorgänge blockieren. Der Zweck der Snapshot-Isolierung besteht darin, das Blockieren von Lesevorgängen zu verhindern .
quelle
Versuchen Sie es (Updlock, Rowlock)
quelle
Die vollständige Antwort könnte sich mit den Interna des DBMS befassen. Dies hängt davon ab, wie die Abfrage-Engine (die den vom SQL-Optimierer generierten Abfrageplan ausführt) funktioniert.
Eine mögliche Erklärung (die zumindest für einige Versionen eines DBMS gilt - nicht unbedingt für MS SQL Server) ist jedoch, dass die ID-Spalte keinen Index enthält, sodass jeder Prozess, der versucht, eine Abfrage mit '
WHERE id = ?
' darin zu bearbeiten , am Ende ausgeführt wird Ein sequentieller Scan der Tabelle, und dieser sequentielle Scan trifft die Sperre, die Ihr Prozess angewendet hat. Sie können auch auf Probleme stoßen, wenn das DBMS standardmäßig Sperren auf Seitenebene anwendet. Durch das Sperren einer Zeile werden die gesamte Seite und alle Zeilen auf dieser Seite gesperrt.Es gibt einige Möglichkeiten, wie Sie dies als Problemquelle entlarven können. Sehen Sie sich den Abfrageplan an. studiere die Indizes; Versuchen Sie Ihr SELECT mit der ID 1000000 anstelle von 1 und prüfen Sie, ob andere Prozesse noch blockiert sind.
quelle
Vielleicht könnte es gelöst werden, wenn mvcc permanent ist (im Gegensatz zu nur einer bestimmten Charge: SET TRANSACTION ISOLATION LEVEL SNAPSHOT):
ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;
[EDIT: 14. Oktober]
Nach dem Lesen: Bessere Parallelität in Oracle als SQL Server? und dies: http://msdn.microsoft.com/en-us/library/ms175095.aspx
Ich bin zu dem Schluss gekommen, dass Sie zwei Flags setzen müssen, um das MVCC von mssql dauerhaft in einer bestimmten Datenbank zu aktivieren:
ALTER DATABASE yourDbNameHere SET ALLOW_SNAPSHOT_ISOLATION ON; ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;
quelle
OK, eine einzelne Auswahl verwendet standardmäßig die Transaktionsisolation "Read Committed", die das Schreiben in diesen Satz sperrt und daher stoppt. Sie können die Transaktionsisolationsstufe mit ändern
Set Transaction Isolation Level { Read Uncommitted | Read Committed | Repeatable Read | Serializable } Begin Tran Select ... Commit Tran
Diese werden in SQL Server BOL ausführlich erläutert
Ihr nächstes Problem ist, dass SQL Server 2K5 die Sperren standardmäßig eskaliert, wenn Sie mehr als ~ 2500 Sperren haben oder mehr als 40% des "normalen" Speichers in der Sperrtransaktion verwenden. Die Eskalation geht zur Seite und dann zur Tabellensperre
Sie können diese Eskalation ausschalten, indem Sie das "Trace-Flag" 1211t setzen. Weitere Informationen finden Sie unter BOL
quelle
Erstellen Sie ein gefälschtes Update, um den Rowlock zu erzwingen.
UPDATE <tablename> (ROWLOCK) SET <somecolumn> = <somecolumn> WHERE id=1
Wenn das Ihre Reihe nicht sperrt, weiß Gott, was wird.
Nach diesem "
UPDATE
" können Sie IhreSELECT (ROWLOCK)
und nachfolgende Updates durchführen.quelle
Ich gehe davon aus, dass Sie nicht möchten, dass eine andere Sitzung die Zeile lesen kann, während diese spezielle Abfrage ausgeführt wird ...
Wenn Sie SELECT in eine Transaktion einschließen, während Sie den Sperrhinweis WITH (XLOCK, READPAST) verwenden, erhalten Sie die gewünschten Ergebnisse. Stellen Sie einfach sicher, dass diese anderen gleichzeitigen Lesevorgänge NICHT WITH (NOLOCK) verwenden. Mit READPAST können andere Sitzungen dasselbe SELECT ausführen, jedoch in anderen Zeilen.
BEGIN TRAN SELECT * FROM <tablename> WITH (XLOCK,READPAST) WHERE RowId = @SomeId -- Do SOMETHING UPDATE <tablename> SET <column>=@somevalue WHERE RowId=@SomeId COMMIT
quelle
Frage - Ist dieser Fall nachweislich das Ergebnis einer Sperreneskalation (dh wenn Sie mit dem Profiler nach Sperreneskalationsereignissen suchen, ist dies definitiv der Grund für die Blockierung)? In diesem Fall gibt es eine vollständige Erklärung und eine (ziemlich extreme) Problemumgehung, indem ein Ablaufverfolgungsflag auf Instanzebene aktiviert wird, um eine Eskalation der Sperren zu verhindern. Siehe http://support.microsoft.com/kb/323630 Trace-Flag 1211
Dies wird jedoch wahrscheinlich unbeabsichtigte Nebenwirkungen haben.
Wenn Sie eine Zeile absichtlich sperren und über einen längeren Zeitraum sperren, ist die Verwendung des internen Sperrmechanismus für Transaktionen nicht die beste Methode (zumindest in SQL Server). Die gesamte Optimierung in SQL Server ist auf kurze Transaktionen ausgerichtet - einsteigen, aktualisieren, aussteigen. Das ist in erster Linie der Grund für die Eskalation von Sperren.
Wenn Sie also eine Zeile über einen längeren Zeitraum "auschecken" möchten, verwenden Sie anstelle der Transaktionssperre am besten eine Spalte mit Werten und eine einfache alte Aktualisierungsanweisung, um die Zeilen als gesperrt zu kennzeichnen oder nicht.
quelle
Anwendungssperren sind eine Möglichkeit, Ihre eigenen Sperren mit benutzerdefinierter Granularität zu rollen und gleichzeitig eine "hilfreiche" Eskalation von Sperren zu vermeiden. Siehe sp_getapplock .
quelle
Versuchen Sie es mit:
SELECT * FROM <tablename> WITH ROWLOCK XLOCK HOLDLOCK
Dies sollte die Sperre exklusiv machen und für die Dauer der Transaktion halten.
quelle
Gemäß diesem Artikel besteht die Lösung darin, den WITH-Hinweis (REPEATABLEREAD) zu verwenden.
quelle
Überprüfen Sie alle Ihre Abfragen erneut. Möglicherweise haben Sie eine Abfrage, die ohne ROWLOCK / FOR UPDATE-Hinweis aus derselben Tabelle ausgewählt wurde, in der Sie SELECT FOR UPDATE haben.
MSSQL eskaliert diese Zeilensperren häufig zu Sperren auf Seitenebene (selbst Sperren auf Tabellenebene, wenn Sie keinen Index für das Feld haben, das Sie abfragen). Weitere Informationen finden Sie in dieser Erklärung . Da Sie nach FOR UPDATE fragen, kann ich davon ausgehen, dass Sie eine Robustheit auf Transacion-Ebene (z. B. Finanzen, Inventar usw.) benötigen. Der Rat auf dieser Website gilt also nicht für Ihr Problem. Es ist nur eine Einsicht, warum MSSQL Sperren eskaliert .
Wenn Sie bereits MSSQL 2005 (und höher) verwenden, sind diese MVCC-basiert. Ich denke, Sie sollten kein Problem mit der Sperre auf Zeilenebene haben, wenn Sie den ROWLOCK / UPDLOCK-Hinweis verwenden. Wenn Sie jedoch bereits MSSQL 2005 und höher verwenden, versuchen Sie, einige Ihrer Abfragen zu überprüfen, die dieselbe Tabelle abfragen, die Sie FOR UPDATE möchten, wenn sie Sperren eskalieren, indem Sie die Felder in ihrer WHERE-Klausel überprüfen, wenn sie einen Index haben.
PS
Ich benutze PostgreSQL, es verwendet auch MVCC haben FOR UPDATE, ich stoße nicht auf das gleiche Problem. Sperreneskalationen werden von MVCC gelöst. Daher wäre ich überrascht, wenn MSSQL 2005 weiterhin Sperren für Tabellen mit WHERE-Klauseln eskalieren würde, die keinen Index für ihre Felder haben. Wenn dies (Sperreneskalation) bei MSSQL 2005 immer noch der Fall ist, versuchen Sie, die Felder in den WHERE-Klauseln zu überprüfen, wenn sie einen Index haben.
Haftungsausschluss: Meine letzte Verwendung von MSSQL ist nur Version 2000.
quelle
Sie müssen die Ausnahme zum Zeitpunkt des Festschreibens behandeln und die Transaktion wiederholen.
quelle
Ich habe das Rowlock-Problem auf ganz andere Weise gelöst. Mir wurde klar, dass SQL Server eine solche Sperre nicht zufriedenstellend verwalten konnte. Ich habe mich entschieden, dies aus programmatischer Sicht mithilfe eines Mutex zu lösen ... waitForLock ... releaseLock ...
quelle
Haben Sie READPAST ausprobiert?
Ich habe UPDLOCK und READPAST zusammen verwendet, um eine Tabelle wie eine Warteschlange zu behandeln.
quelle
Wie wäre es, wenn Sie zuerst versuchen würden, ein einfaches Update für diese Zeile durchzuführen (ohne wirklich Daten zu ändern)? Danach können Sie mit der Zeile fortfahren, in der die Aktualisierung ausgewählt wurde.
UPDATE dbo.Customer SET FieldForLock = FieldForLock WHERE CustomerID = @CustomerID /* do whatever you want */
Bearbeiten : Sie sollten es natürlich in eine Transaktion einschließen
Bearbeiten 2 : Eine andere Lösung besteht darin, die Isolationsstufe SERIALIZABLE zu verwenden
quelle