Aufforderung, keine Transaktionen zu verwenden und eine Problemumgehung zum Simulieren einer Transaktion zu verwenden


43

Ich habe T-SQL seit mehreren Jahren entwickelt und beschäftige mich ständig weiter mit allen Aspekten der Sprache. Ich habe vor kurzem angefangen, in einem neuen Unternehmen zu arbeiten, und habe einen meiner Meinung nach merkwürdigen Vorschlag für Transaktionen erhalten. Benutze sie niemals. Verwenden Sie stattdessen eine Problemumgehung, die eine Transaktion simuliert. Dies kommt von unserem DBA, der in einer Datenbank mit vielen Transaktionen und anschließend vielen Blockierungen arbeitet. Die Datenbank, in der ich hauptsächlich arbeite, ist von diesem Problem nicht betroffen, und ich sehe, dass in der Vergangenheit Transaktionen verwendet wurden.

Ich verstehe, dass das Blockieren von Transaktionen erwartet wird, da dies in der Natur der Sache liegt. Aber ich habe viele Gelegenheiten, in denen jede Anweisung erfolgreich ausgeführt werden muss. Wenn einer ausfällt, müssen alle das Commit nicht ausführen.

Ich habe den Umfang meiner Transaktionen immer so eng wie möglich gehalten, immer in Verbindung mit SET XACT_ABORT ON und immer innerhalb eines TRY / CATCH.

Beispiel:

CREATE SCHEMA someschema;
GO


CREATE TABLE someschema.tableA
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColA VARCHAR(10) NOT NULL
);
GO

CREATE TABLE someschema.tableB
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColB VARCHAR(10) NOT NULL
); 
GO


CREATE PROCEDURE someschema.ProcedureName @ColA VARCHAR(10), 
                                          @ColB VARCHAR(10)
AS
SET NOCOUNT, XACT_ABORT ON;
BEGIN
BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);

--Implement error
    SELECT 1/0 

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@trancount > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END;
    THROW;
    RETURN;
END CATCH;
END;
GO

Hier ist, was sie mir vorgeschlagen haben.

GO



CREATE PROCEDURE someschema.ProcedureNameNoTransaction @ColA VARCHAR(10), 
                                                       @ColB VARCHAR(10)
AS
SET NOCOUNT ON;
BEGIN
BEGIN TRY
    DECLARE @tableAid INT;
    DECLARE @tableBid INT;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);
    SET @tableAid = SCOPE_IDENTITY();

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);
    SET @tableBid = SCOPE_IDENTITY();

--Implement error
    SELECT 1/0 

END TRY
BEGIN CATCH
    DELETE FROM someschema.tableA
    WHERE id = @tableAid;

    DELETE FROM someschema.tableB
    WHERE id = @tableBid;

    THROW;

    RETURN;
END CATCH;
END;
GO

Meine Frage an die Community lautet wie folgt. Ist dies als praktikable Abhilfemaßnahme für Transaktionen sinnvoll?

Meine Meinung nach weiß ich über Transaktionen Bescheid und die Lösung schlägt vor, nein, dies ist keine praktikable Lösung und führt viele Fehlerquellen ein.

In der vorgeschlagenen Problemumgehung werden vier implizite Transaktionen angezeigt. Die zwei Einfügungen im Versuch und dann zwei weitere Transaktionen für die Löschungen im Fang. Die Einfügungen werden zwar rückgängig gemacht, jedoch ohne dass ein Rollback ausgeführt wird, sodass tatsächlich kein Rollback ausgeführt wird.

Dies ist ein sehr einfaches Beispiel, um das vorgeschlagene Konzept zu demonstrieren. Einige der tatsächlich gespeicherten Prozeduren, in denen ich dies getan habe, machen sie sehr lang und schwierig zu verwalten, da das Zurücksetzen mehrerer Ergebnismengen gegenüber zwei Parameterwerten in diesem Beispiel ziemlich kompliziert wird, wie Sie sich vorstellen können. Da "Zurückrollen" jetzt manuell gemacht wird, besteht die Möglichkeit, da echt etwas zu verpassen.

Ein weiteres Problem, das meines Erachtens besteht, sind Zeitüberschreitungen oder unterbrochene Verbindungen. Wird dies immer noch rückgängig gemacht? Nach meinem Verständnis sollte SET XACT_ABORT ON verwendet werden, damit in diesen Fällen die Transaktion zurückgesetzt wird.

Vielen Dank für Ihr Feedback im Voraus!


4
Kommentare, die nicht ihrem angegebenen Zweck entsprechen , wurden gelöscht oder in die Community-Wiki-Antwort verschoben.
Paul White

Antworten:


61

Sie können nicht nicht - Transaktionen in SQL Server verwenden (und wahrscheinlich auch andere geeignete RDBMS). Ohne explizite Transaktionsgrenzen ( begin transaction... commit) startet jede SQL-Anweisung eine neue Transaktion, die implizit festgeschrieben wird (oder zurückgesetzt wird), nachdem die Anweisung abgeschlossen ist (oder fehlschlägt).

Die von der Person, die sich als "DBA" ausgibt, vorgeschlagene Transaktionssimulation stellt drei von vier erforderlichen Eigenschaften der Transaktionsverarbeitung nicht sicher, da sie nur "weiche" Fehler behandelt und nicht in der Lage ist, mit "harten" Fehlern umzugehen. B. Netzwerktrennungen, Stromausfälle, Festplattenfehler usw.

  • Atomarität: scheitern. Wenn irgendwo in der Mitte Ihrer Pseudotransaktion ein "schwerer" Fehler auftritt, ist die Änderung nicht atomar.

  • Konsistenz: scheitern. Daraus folgt, dass sich Ihre Daten nach einem "harten" Fehler in einem inkonsistenten Zustand befinden.

  • Isolation: scheitern. Es ist möglich, dass eine gleichzeitige Pseudotransaktion einige der durch Ihre Pseudotransaktion geänderten Daten ändert , bevor Ihre abgeschlossen ist.

  • Haltbarkeit: Erfolg. Änderungen, die Sie vornehmen , sind dauerhaft. Der Datenbankserver stellt dies sicher. Dies ist das einzige, was der Ansatz Ihres Kollegen nicht vermasseln kann.

Sperren sind eine weit verbreitete und empirisch erfolgreiche Methode zur Sicherstellung der ACIDity von Transaktionen aller Art oder RDBMS (diese Site ist ein Beispiel). Ich halte es für sehr unwahrscheinlich, dass ein zufälliger DBA eine bessere Lösung für das Nebenläufigkeitsproblem finden kann als Hunderte, möglicherweise Tausende von Informatikern und Ingenieuren, die in den letzten 50 Jahren einige interessante Datenbanksysteme aufgebaut haben. 60 Jahre? (Mir ist klar, dass dies als Argument der "Berufung auf die Autorität" etwas trügerisch ist, aber ich werde mich trotzdem daran halten.)

Ignorieren Sie abschließend den Rat Ihres "DBA", wenn Sie können, bekämpfen Sie ihn, wenn Sie den Geist haben, und kehren Sie hierher zurück, wenn bestimmte Probleme mit der Parallelität auftreten.


14

Es gibt einige Fehler, die so schwerwiegend sind, dass der CATCH-Block niemals eingegeben wird. Aus der Dokumentation

Fehler mit einem Schweregrad von 20 oder höher, die die Verarbeitung des SQL Server-Datenbankmoduls für die Sitzung beenden. Wenn ein Fehler mit einem Schweregrad von 20 oder höher auftritt und die Datenbankverbindung nicht unterbrochen wird, behandelt TRY ... CATCH den Fehler.

Aufmerksamkeiten wie Client-Interrupt-Anforderungen oder unterbrochene Client-Verbindungen.

Wenn die Sitzung von einem Systemadministrator mithilfe der Anweisung KILL beendet wird.

...

Kompilieren Sie Fehler, z. B. Syntaxfehler, die die Ausführung eines Stapels verhindern.

Fehler, die aufgrund einer verzögerten Namensauflösung auftreten.

Viele davon lassen sich einfach mit dynamischem SQL erstellen. Rückgängig gemachte Anweisungen schützen Ihre Daten nicht vor solchen Fehlern.


2
Richtig - und wenn nicht anders angegeben, würde der Client, der während der Ausführung des Codes stirbt, einen Fehler darstellen, "der so schwerwiegend ist, dass der CATCH-Block niemals betreten wird". Ganz gleich, wie sehr Sie der Software vertrauen (nicht nur Ihrem eigenen Code, sondern JEDEM Teil ALLER beteiligten Software-Stacks), es besteht immer die Möglichkeit, dass ein Hardwarefehler (erneut, möglicherweise irgendwo entlang der Kette) Sie jederzeit vor Kälte bewahrt . Dies zu berücksichtigen, ist eine gute Verteidigung gegen das freundliche Denken, das zu einer solchen "Problemumgehung" führt.
13.

2
Sie könnten auch ein Deadlock-Opfer sein. Ihre CATCH-Blöcke werden ausgeführt, aber ausgelöst, wenn sie versuchen, in die Datenbank zu schreiben.
Joshua,

10

i-one : Die Ihnen vorgeschlagene Problemumgehung ermöglicht (zumindest) die Verletzung von "A" der ACID . Wenn der SP beispielsweise von einem Remoteclient ausgeführt wird und die Verbindung unterbrochen wird, kann ein teilweises "Festschreiben" / "Zurücksetzen" erfolgen, da der Server die Sitzung zwischen zwei Einfügungen / Löschungen beenden kann(und die Ausführung des SP abbrechen kann, bevor das Ende erreicht ist). .

Ist dies als praktikable Abhilfemaßnahme für Transaktionen sinnvoll?

dan-guzman : Nein, derCATCHBlock wird im Falle eines Abfrage-Timeouts nie ausgeführt, da die Client-API den Batch abgebrochen hat. Ohne eine TransaktionSET XACT_ABORT ONkann nur die aktuelle Anweisung zurückgesetzt werden.

tibor-karaszi : Sie haben 4 Transaktionen, dh mehr Protokollierung in der Transaktionsprotokolldatei. Denken Sie daran, dass jede Transaktion ein synchrones Schreiben der Protokollsätze bis zu diesem Zeitpunkt erfordert, dh, Sie erzielen auch unter diesem Aspekt eine schlechtere Leistung, wenn Sie viele Transaktionen verwenden.

rbarryyoung : Wenn sie häufig blockiert werden, müssen sie entweder ihr Datendesign korrigieren , ihre Tabellenzugriffsreihenfolge rationalisieren oder eine geeignetere Isolationsstufe verwenden. Sie gehen davon aus, dass ihre Probleme (und Unverständnis) zu Ihrem Problem werden. Die Beweise aus Millionen anderer Datenbanken sind, dass dies nicht der Fall sein wird.

Was sie versuchen, manuell zu implementieren, ist effektiv eine optimistische Nebenläufigkeit. Stattdessen sollten sie eine der besten optimistischen Parallelitäten der Welt verwenden, die bereits in SQL Server integriert sind. Dies geht zum Isolationspunkt oben. Höchstwahrscheinlich müssen sie von der aktuell verwendeten pessimistischen Parallelitätsisolationsstufe zu einer der optimistischen Parallelitätsisolationsstufen wechseln SNAPSHOToder READ_COMMITTED_SNAPSHOT. Diese tun effektiv dasselbe wie ihr manueller Code, es sei denn, es wird korrekt ausgeführt.

ross-presser : Wenn Sie extrem lange laufende Prozesse haben - wie es heute und in der nächsten Woche passiert, muss sich etwas ändern, und wenn die Sache in der nächsten Woche scheitert, muss die heutige rückwirkend scheitern - möchten Sie sich vielleicht mit Sagen befassen . Genau genommen liegt dies außerhalb der Datenbank, da ein Servicebus erforderlich ist.


5

Schlechter Ideencode wird in der Regel teurer.

Wenn bei der Verwendung einer expliziten Transaktion (Rollback / Commit) Blockierungsprobleme auftreten, verweisen Sie Ihren DBA auf das Internet, um einige gute Ideen zur Behebung der Probleme zu erhalten.

Hier ist eine Möglichkeit, die Blockierung zu verringern : https://www.sqlservercentral.com/articles/using-indexes-to-reduce-blocking-in-concurrent-transactions

Indizes reduzieren die Anzahl der Suchvorgänge, die in einer Tabelle / Seite ausgeführt werden müssen, um eine Zeile / einen Satz von Zeilen zu finden. Sie werden im Allgemeinen als Methode zur Verkürzung der Ausführungszeiten für SELECT * -Abfragen und zu Recht angesehen. Sie sind nicht für Tabellen geeignet, die an einer großen Anzahl von UPDATES beteiligt sind. Tatsächlich erweisen sich INDEXES in diesen Fällen als ungünstig, da sie die Zeit zum Ausführen von UPDATE-Abfragen verlängern.

Dies ist jedoch nicht immer der Fall. Wenn wir uns ein wenig mit der Ausführung einer UPDATE-Anweisung befassen, müssen wir auch zuerst eine SELECT-Anweisung ausführen. Dies ist ein spezielles und häufig vorkommendes Szenario, in dem Abfragen sich gegenseitig ausschließende Zeilengruppen aktualisieren. INDEXES können hier entgegen der landläufigen Meinung zu einer signifikanten Leistungssteigerung der Datenbank-Engine führen.


4

Die falsche Transaktionsstrategie ist gefährlich, da sie Parallelitätsprobleme zulässt, die Transaktionen speziell verhindern. Beachten Sie, dass im zweiten Beispiel alle Daten zwischen Anweisungen geändert werden können.

Die gefälschten Transaktionslöschvorgänge sind nicht GARANTIERT, um ausgeführt zu werden oder erfolgreich zu sein. Wenn der Datenbankserver während der gefälschten Transaktion ausgeschaltet wird, bleiben einige, aber nicht alle Effekte erhalten. Es wird auch nicht garantiert, dass sie so erfolgreich sind, wie ein Transaktions-Rollback ist.

Diese Strategie funktioniert möglicherweise mit Einfügungen, aber definitiv nicht mit Aktualisierungen oder Löschungen (keine SQL-Anweisungen für Zeitmaschinen).

Wenn eine strikte Parallelität von Transaktionen zum Blockieren führt, gibt es viele Lösungen, auch solche, die die Schutzstufe verringern. Dies ist der richtige Weg, um das Problem zu lösen.

Ihr Datenbankadministrator bietet eine Lösung an, die möglicherweise einwandfrei funktioniert, wenn nur ein Benutzer der Datenbank vorhanden ist, die jedoch für jede ernsthafte Verwendung absolut ungeeignet ist.


4

Hierbei handelt es sich nicht um ein Programmierproblem, sondern um ein Problem zwischen Personen / Missverständnissen. Höchstwahrscheinlich macht sich Ihr "DBA" Sorgen um Sperren, nicht um Transaktionen.

Die anderen Antworten erklären bereits, warum Sie Transaktionen verwenden müssen ... Ich meine, das ist es, was RDBMS tut, ohne ordnungsgemäß verwendete Transaktionen gibt es keine Datenintegrität. Ich werde mich daher darauf konzentrieren, wie das eigentliche Problem zu lösen ist: Finden Sie heraus, warum Ihr "DBA" entwickelte eine Allergie gegen Transaktionen und überzeugte ihn, seine Meinung zu ändern.

Ich denke, dieser Typ verwechselt "ein bestimmtes Szenario, in dem schlechter Code zu einer schrecklichen Leistung führte" mit "alle Transaktionen sind schlecht". Ich würde nicht erwarten, dass ein kompetenter DBA diesen Fehler begeht, das ist wirklich komisch. Vielleicht hatte er eine wirklich schlechte Erfahrung mit einem schrecklichen Code?

Stellen Sie sich ein Szenario wie dieses vor:

BEGIN
UPDATE or DELETE some row, which takes locks it
...do something that takes a while
...perform other queries
COMMIT

Diese Art der Transaktionsverwendung enthält eine Sperre (oder mehrere Sperren), was bedeutet, dass andere Transaktionen, die dieselben Zeilen treffen, warten müssen. Wenn die Sperren über einen längeren Zeitraum bestehen und insbesondere viele andere Transaktionen dieselben Zeilen sperren möchten, kann dies die Leistung erheblich beeinträchtigen.

Was Sie tun könnten, ist ihn zu fragen, warum er die seltsam falsche Vorstellung hat, keine Transaktionen zu verwenden, welche Arten von Abfragen problematisch waren usw. Versuchen Sie dann, ihn davon zu überzeugen, dass Sie ähnliche schlechte Szenarien auf jeden Fall vermeiden, dass Sie Ihre Sperrverwendung überwachen und Leistung, beruhigen ihn, etc.

Was er dir sagt ist "Fass den Schraubenzieher nicht an!" Der Code, den Sie in Ihrer Frage gepostet haben, verwendet im Grunde genommen einen Hammer, um eine Schraube anzutreiben. Viel besser ist es, ihn davon zu überzeugen, wie man einen Schraubenzieher benutzt ...

Ich kann mir einige Beispiele vorstellen ... nun, sie waren auf MySQL, aber das sollte auch funktionieren.

Es gab ein Forum, in dem die Aktualisierung des Volltextindex eine Weile gedauert hat. Wenn ein Benutzer einen Beitrag übermittelt, aktualisiert die Transaktion die Thementabelle, um die Anzahl der Beiträge und das Datum des letzten Beitrags zu erhöhen (wodurch die Themenzeile gesperrt wird), fügt dann den Beitrag ein und die Transaktion hält die Sperre, bis die Aktualisierung des Volltextindex abgeschlossen ist und das COMMIT war erledigt.

Da dies auf einem Rustbucket mit viel zu wenig RAM lief, führte die Aktualisierung des Volltextindex häufig zu mehreren Sekunden intensiver zufälliger Eingabe auf dem einzelnen langsam drehenden Laufwerk in der Box.

Das Problem bestand darin, dass Benutzer, die auf das Thema klickten, durch eine Abfrage die Anzahl der Aufrufe für das Thema erhöhten, was auch eine Sperre für die Themenzeile erforderte. Daher konnte niemand das Thema anzeigen, während der Volltextindex aktualisiert wurde. Ich meine, die Zeile könnte gelesen werden, aber die Aktualisierung würde sperren.

Schlimmer noch, durch das Posten wurde die Anzahl der Posts in der übergeordneten Forentabelle aktualisiert und die Sperre beibehalten, während der Volltextindex aktualisiert wurde. Dadurch wurde das gesamte Forum für einige Sekunden eingefroren und es stauten sich Unmengen von Anfragen in der Webserver-Warteschlange auf .

Die Lösung bestand darin, die Sperren in der richtigen Reihenfolge zu setzen: BEGINNEN, den Beitrag einzufügen und den Volltextindex zu aktualisieren, ohne Sperren zu setzen, und dann die Themen- / Forumzeilen mit der Anzahl der Beiträge und dem Datum des letzten Beitrags sowie COMMIT schnell zu aktualisieren. Das hat das Problem komplett gelöst. Es ging nur um ein paar Fragen, ganz einfach.

In diesem Fall waren Transaktionen nicht das Problem ... Vor einer längeren Operation wurde eine unnötige Sperre aktiviert. Andere Beispiele für Dinge, die Sie vermeiden sollten, während Sie eine Transaktion sperren: Warten auf Benutzereingaben, Zugriff auf viele nicht zwischengespeicherte Daten von langsam drehenden Laufwerken, Netzwerk-E / A usw.

Natürlich haben Sie manchmal keine Wahl und müssen eine lange Verarbeitung durchführen, während Sie umständliche Sperren halten. Hier gibt es Tricks (Bearbeiten einer Kopie der Daten usw.), aber der Leistungsengpass wird häufig durch eine Sperre verursacht, die nicht absichtlich aktiviert wurde. Durch einfaches Neuanordnen von Abfragen wird das Problem behoben. Noch besser ist es, sich der Sperren bewusst zu sein, die beim Schreiben der Abfragen vorgenommen wurden ...

Ich werde die anderen Antworten nicht wiederholen, aber wirklich ... Transaktionen verwenden. Ihr Problem besteht darin, Ihren "DBA" zu überzeugen und nicht das wichtigste Merkmal einer Datenbank zu umgehen ...


3

TLDR: Verwenden Sie die richtige Isolationsstufe .

Wie Sie richtig bemerkt haben, kann der Ansatz ohne Transaktionen und mit "manueller" Wiederherstellung sehr komplex sein. Die hohe Komplexität bedeutet normalerweise viel mehr Zeit für die Implementierung und viel mehr Zeit für die Fehlerbehebung (da Komplexität zu mehr Fehlern bei der Implementierung führt). Dies bedeutet, dass ein solcher Ansatz Ihren Kunden viel mehr kosten kann.

Das Hauptanliegen Ihres "dba" -Kollegen ist die Leistung. Eine Möglichkeit zur Verbesserung besteht darin, die richtige Isolationsstufe zu verwenden. Angenommen, Sie haben eine Prozedur, die dem Benutzer eine Art Übersichtsdaten zur Verfügung stellt. Diese Prozedur muss nicht unbedingt die Isolationsstufe SERIALIZABLE verwenden. In vielen Fällen kann READ UNCOMMITTED durchaus ausreichen. Dies bedeutet, dass ein solcher Vorgang nicht durch Ihre Transaktion blockiert wird, durch die Daten erstellt oder geändert werden.

Ich würde vorschlagen, dass Sie alle vorhandenen Funktionen / Prozeduren in Ihrer Datenbank überprüfen, die angemessene Isolationsstufe für jede bewerten und Ihrem Kunden die Leistungsvorteile erläutern. Passen Sie dann diese Funktionen / Verfahren entsprechend an.


2

Sie können auch In-Memory-OLTP-Tabellen verwenden. Sie benutzen natürlich immer noch Transaktionen, aber es gibt keine Blockierung.
Anstatt alle Vorgänge erfolgreich zu blockieren, wird die Engine während der Festschreibungsphase nach Transaktionskonflikten suchen und eine der Festschreibungen schlägt möglicherweise fehl. Microsoft verwendet den Begriff "Optimistic Locking".
Wenn das Skalierungsproblem durch einen Konflikt zwischen zwei Schreibvorgängen verursacht wird, z. B. zwei gleichzeitige Transaktionen, die versuchen, dieselbe Zeile zu aktualisieren, lässt In-Memory OLTP eine Transaktion erfolgreich ablaufen und die andere Transaktion schlägt fehl. Die fehlgeschlagene Transaktion muss entweder explizit oder implizit erneut gesendet werden, und die Transaktion muss erneut versucht werden.
Weiteres unter: In Memory OLTP


-5

Es gibt eine Möglichkeit, Transaktionen in begrenztem Umfang zu verwenden, indem Sie Ihr Datenmodell so ändern, dass es objektorientierter ist. Anstatt beispielsweise demografische Daten einer Person in mehreren Tabellen zu speichern und sie miteinander in Beziehung zu setzen und Transaktionen zu erfordern, können Sie ein einziges JSON-Dokument haben, in dem alles, was Sie über diese Person wissen, in einem einzigen Feld gespeichert ist. Natürlich ist es eine weitere Herausforderung, die Domain-Ausdehnung zu ermitteln, am besten von Entwicklern und nicht von Datenbankadministratoren

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.