2015-05-21 11 views
16

Meine Vertrautheit ist mit der Microsoft SQL-Server-Welt mit ADO (dbGo), und ich habe viele Anwendungen für diese Umgebung geschrieben. Jetzt habe ich eine ältere Delphi 7-Anwendung mit einer Firebird 2.5-Datenbank, die ich pflegen muss.Wie Firebird Client-Anwendung warten auf Zeile zum entsperren

aber ich bin zu finden, ist, dass, wenn 2-Client-Anwendungen ausführen dieses:

SQLQuery.SQL.Text := 'Update mytable set field1 = 11 where keyfield = 99' 
SQLQuery.Execute; 

bei fast genau die gleiche Zeit, die zweite Anwendung sofort ein „Deadlock“ Fehler bekommt. In SQL Server gibt es eine Wartezeit

ADOConnection.Isolationlevel = ilCursorstability; 
ADOConnection.CommandTimeout := 5; 

bevor jede Ausnahme in der zweiten Clientanwendung ausgelöst wird. Die Ausnahmebehandlung kann einen Rollback in einer Situation enthalten, die in einem Stapelprozess als sehr ungewöhnlich angesehen wird. Dies ist vernünftig. 5 Sekunden sind eine schrecklich lange Zeit in der Computerverarbeitungszeit.

Jetzt sind meine Versuche, die gleiche Methodik beim Firebird Client zu verwenden, erfolglos, weil der "Deadlock" (eigentlich ein Datensatz in Verwendung) sofort auftritt. Wenn das Datenbankmodul nicht so konfiguriert werden kann, dass es auf Zustände zur Verbesserung wartet (Sperren von Datensätzen wird freigegeben), muss die Verantwortung nun beim Entwickler der Clientanwendung liegen, der wahnsinnig langsamen Code schreiben muss, um das zu beseitigen, was zu erscheinen scheint Ich bin ein großer Fehler von Firebird.

Sobald die „Deadlock“ erkannt wurde, hat die Bedingung nicht klar, außer durch die Verbindungskomponente trennen

while rowsupdated = 0 and counter < 5 do 
begin 
    try 
    rowsupdated := SQLQuery.Execute; 
    except 
    SQLConnection.Connected := False; 
    SQLConnection.Connected := True; 
    end; 
    Inc(Counter) 
end; 

Wie stellen Sie robuste Multi-User-Table-Update-Clients, wenn Sie nicht haben, jede wesentliche Lock-Toleranz in Firebird, mit DBX in Delphi?

+0

Haben Sie FirebirdSQL schon länger nicht mehr verwendet, aber denken Sie daran, dass es eine Funktion SELECT UPDATE WITH LOCK gab, die stattdessen auf SQL-Ebene verwendet werden konnte. Überprüfen Sie dies: http://www.firebirdsql.org/refdocs/langrefupd25-notes-withlock.html – quasoft

+0

Der Standardwert für den IsolationLevel für eine DBExpress-Verbindung zu Interbase ist 'ReadCommitted', was" ilCursorstabilität "entspricht. Das 'CommandTImeout' existiert nicht, aber es gibt' WaitOnLocks', das standardmäßig auf 'True' steht und bedeutet * Gibt an, dass eine Transaktion auf den Zugriff wartet, wenn sie einen Sperrkonflikt mit einer anderen Transaktion * (gemäß den Dokumenten) feststellt. Beide sind in den Verbindungsparametern festgelegt. –

+0

Trotz all meiner Versuche, den Client anders zu konfigurieren, scheint er standardmäßig 'nowait' zu sein. Siehe meine Frage zu @TOndrej unten. – nolaspeaker

Antwort

7

Der Client kann angeben, ob die Transaktion auf die Deadlock-Auflösung warten soll. Wenn in Ihrem Fall der Deadlock sofort passiert, liegt das wahrscheinlich an Ihrer Konfiguration (unter Verwendung des Transaktionsparameters nowait auf dem Client). Die Verwendung von nowait führt dazu, dass die Serverseite einen Deadlock erkennt und (nach einem konfigurierbaren Timeout) eine Ausnahme auf dem Client auslöst.

Seit Firebird 2.0 können Sie auch ein Sperrzeitlimit für eine Transaktion vom Client angeben und den vom Server konfigurierten Zeitüberschreitungswert überschreiben.

+0

Können Sie bitte genauer sein? Wie geben Sie den Sperrzeitlimitwert für eine bestimmte Anwendung auf dem Client an? – nolaspeaker

+0

@nolaspeaker Es hängt von der Client-Bibliothek Ihrer Wahl und davon ab, wie es den Transaktionsparameterblock (TPB) verfügbar macht, der auf der Firebird-Client-API-Ebene eingerichtet werden muss. –

+0

Ich sehe, Sie verwenden DBX. Welche Version/Delphi-Version? –

3

Die Firebird-Transaktion kann so konfiguriert werden, dass sie entweder jetzt ist oder wartet (mit oder ohne eine bestimmte Zeitüberschreitung). Wie dies konfiguriert werden kann, hängt vom Treiber ab, und da ich mit Delphi nicht vertraut bin, kann ich dazu nichts sagen. Nowait ist normalerweise der Standard, da das Warten in den meisten Fällen nur das Unvermeidliche verzögert.

Der "Deadlock" -Fehler ist ein bisschen falsch, da es in der normalen Parallelsprache kein Deadlock ist. Der zweite Teil des Fehlers ist normalerweise aussagekräftiger, zB Update-Konflikte bei gleichzeitiger Aktualisierung., ist es ein historisches Artefakt, dass es unter "Deadlock" gruppiert ist (obwohl in den meisten Fällen dieser Fehler bedeutet, dass Sie Ihre Transaktion neu starten müssen, in diesem Sinne also "tot").

0

Ich benutze die Schaltfläche "Beantworten Sie Ihre eigene Frage". Ich habe eine Lösung gefunden.Installieren

  1. IBPhoenix Opensource-ODBC-Treiber für Firebird/Interbase
  2. Configure ODBC DSN eine Verbindung mit nowait Firebird.fdb abgehakt und LOCKTIMEOUT eingestellt, wie in Sekunden erforderlich. Ich habe 15 Sekunden gewählt.
  3. Verwenden Sie Delphi 7 ADO (dbGo) TADOConnection für die Verwendung von Microsoft OLE DB Provider für ODBC-Treiber konfiguriert.
  4. Dies ist das wichtige Bit: Legen Sie die ADOConnection.TransactionIsolation auf ilReadUncommited oder ilDirtyRead fest.

Was das bedeutet ist, bewirkt, dass die TADOQuery.ExecSQL zu tatsächlich warten (für bis zu den angegebenen 15 Sekunden), wenn er feststellt, dass der Datensatz bereits in einer Transaktion aktualisiert worden, die noch nicht hat oder gerollt zurück!

Dies unterscheidet sich vom DBX-Treiber, der in dieser Situation sofort eine sogenannte "Deadlock" -Ausnahme auslöst. (Wie wir oben erörtert)

Also, wenn beide Abfragen tun dies

Update MYTABLE set NUM = NUM + 1 where keyvalue = 99; 

und der Startwert (vor jeder Aktualisierung) 0 ist, wird der Wert von NUM, nachdem beide Transaktionen begangen haben 2, wie erwartet .

Wieder mit NUM = 0 beginnen. Wenn die erste Transaktion rückgängig gemacht wird, kann die zweite Transaktion festschreiben (oder Rollback). Und der Wert nach der zweiten Aktualisierung hat sich verpflichtet, ist nur 1.

Ich weiß nicht, wie oder warum das so gut funktioniert, zumal Firebird nicht ReadUnComitted oder DirtyRead unterstützen soll, aber ich bin einfach nur glücklich es funktioniert so, wie ich es möchte.