Ich habe einen synthetischen Test, der einige Fehler reproduziert, die wir in der Produktionsumgebung haben. Hier sind die 2 Skripte, um es zu reproduzieren:
1
DBCC TRACEOFF(-1,3604,1200) WITH NO_INFOMSGS;
SET NOCOUNT ON;
IF @@TRANCOUNT > 0 ROLLBACK
IF object_id('test') IS NOT NULL
DROP TABLE test
IF object_id('TMP_test') IS NOT NULL
DROP TABLE TMP_test
IF object_id('test1') IS NOT NULL
DROP TABLE test1
CREATE TABLE test(Id INT IDENTITY PRIMARY KEY)
GO
INSERT test DEFAULT VALUES
GO 2000
WHILE 1 = 1
BEGIN
CREATE TABLE TMP_test(Id INT PRIMARY KEY)
INSERT TMP_test SELECT * FROM test
WAITFOR DELAY '0:00:00.1'
BEGIN TRAN
EXEC sp_rename 'test', 'test1'
EXEC sp_rename 'TMP_test', 'test'
EXEC sp_rename 'test1', 'TMP_test'
DROP TABLE TMP_test
commit
END
2 ..
SET NOCOUNT ON;
DECLARE @c INT
WHILE 1 = 1
BEGIN
SELECT @c = COUNT(*) FROM Test IF @@ERROR <> 0 BREAK
SELECT @c = COUNT(*) FROM Test IF @@ERROR <> 0 BREAK
/* and repeat this 10-20 times more*/
SELECT @c = COUNT(*) FROM Test IF @@ERROR <> 0 BREAK
END
Das Problem ist also, dass ich diese Art von Fehler erhalten kann, wenn ich das erste Skript in einer Sitzung ausführe und es laufen lasse und dann das zweite in einer separaten Sitzung ausführe:
Meldung 208, Ebene 16, Status 1, Zeile 13 Ungültiger Objektname 'Test'.
Die Frage ist - WARUM sehe ich diesen Fehler für den Fall, dass COMMIT
am Ende des 1. Skripts ein Fehler auftritt und ich keinen bekomme, wenn es einen gibt ROLLBACK
?
Ich habe das Gefühl, dass es irgendwie mit der Situation zusammenhängt, wenn das Skript festschreibt, gibt es immer noch die Tabelle mit dem Namen, test
aber es ist ein anderes Objekt und das 2. Skript muss sich neu kompilieren. Und das ist in Ordnung. Aber warum wird der fehlende Tabellenfehler angezeigt? AFAIK - wenn ich die Tabelle innerhalb einer Transaktion umbenenne - hält sie die Sch-M-Sperre bis zum Ende?
Kann mir jemand antworten oder mich zu den technischen Artikeln führen, die ich gründlich lesen und den Grund verstehen kann?
quelle
Antworten:
Wirklich interessante und schwierige Frage. Ich konnte keine offizielle Dokumentation dieses Verhaltens finden, und ich vermute, dass es keine gibt (obwohl ich es lieben würde, wenn mich jemand korrigiert!).
Meine Forschung lässt mich glauben, dass es der Planerstellungsschritt ist, der für diese Rennbedingung anfällig ist. Beachten Sie, dass ich Ihre Testabfragen eine Stunde lang fehlerfrei ausführen konnte. Wenn ich jedoch wiederholt Ihren Prozess starte, wird ein Teil der Zeit sofort ein Fehler angezeigt. Wenn ein Fehler auftritt, geschieht dies bei der Kompilierung des Plans immer sofort. Alternativ können Sie dem COUNT (*) in der Schleife "OPTION RECOMPILE" hinzufügen, um bei jedem Versuch einen neuen Plan zu kompilieren. Bei diesem Ansatz wird der Fehler fast sofort angezeigt, wenn ich Ihre Skripte ausgeführt habe.
Ich war in der Lage, den Fehler mit einer kontrollierten Reihe von Schritten zu reproduzieren, die den Fehler bei jedem Versuch zu treffen scheinen, wodurch die Notwendigkeit beseitigt wurde, eine Schleife einzurichten und sich auf Zufälligkeit zu verlassen.
Ich habe auch einen möglichen Fix eingebaut (um ALTER TABLE ... SWITCH zu verwenden), den Sie möglicherweise in Ihrer Produktionsumgebung ausprobieren können. Nun zu den Details!
Hier sind die Schritte zum Reproduzieren:
Mit diesen Schritten können wir etwas genauer untersuchen, was den Fehler verursacht. Zu diesem Zweck habe ich eine Ablaufverfolgung mit den folgenden Ereignissen ausgeführt: SP: StmtStarting, SP: StmtCompleted, Sperre: Erworben, Sperre: Freigegeben.
Was ich gefunden habe, ist, dass Folgendes in der richtigen Reihenfolge auftritt (wobei dazwischen einige Details weggelassen werden):
Wenn jedoch eine ähnliche Abfolge von Ereignissen auftritt, wenn keine Plankompilierung erforderlich ist (z. B. beim Ausführen Ihrer Schleife), ist SQL Server tatsächlich intelligent genug, um zu erkennen, dass sich die Objekt-ID geändert hat. Ich habe auch diesen Fall verfolgt und eine Situation gefunden, in der der zweite COUNT (*) die folgende Sequenz ausführte
Es sieht also so aus, als ob diese adaptive Logik vorhanden ist, wie Sie vermutet haben. Es sieht jedoch so aus, als ob es nur für die Ausführung von Abfragen und nicht für die Kompilierung von Abfragen vorhanden ist.
Wie versprochen, ist hier ein alternatives Snippet für Abschnitt (3), das eine Möglichkeit zum Umbenennen von Tabellen bietet, mit der das Problem der Parallelität behoben wird (zumindest auf meinem Computer!):
Zum Schluss noch ein weiterer Link, den ich nützlich fand, um mir Gedanken darüber zu machen, wie man im Kontext von Tabellen, deren Namen sich ändern, über das Sperren nachdenken kann:
quelle