[Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 12:46

Hallo,

Wann wird denn eine Tabelle auf der Nativen DB gesperrt?

Ich habe folgenden code:
Code:
WITH Cust DO BEGIN
  RESET;
  IF FINDSET THEN BEGIN   
    REPEAT
      ... tu was
      // Save rec if changed
      IF ChangesMade THEN
        MODIFY;
    UNTIL NEXT = 0;
  END;
END;
COMMIT;


Gruß
Ralf
Zuletzt geändert von ralf5 am 29. September 2010 14:24, insgesamt 4-mal geändert.

Re: Wann wird eine Tabelle gesperrt?

27. September 2010 15:27

Wenn du kein explizites LOCKTABLE verwendest, dann wird eine Tabelle bspw. direkt vor einem INSERT oder MODIFY gesperrt.

Re: Wann wird eine Tabelle gesperrt?

27. September 2010 15:38

HattrickHorst hat geschrieben:Wenn du kein explizites LOCKTABLE verwendest, dann wird eine Tabelle bspw. direkt vor einem INSERT oder MODIFY gesperrt.


Wird die Sperrung dann direkt nach dem Modify wieder aufgehoben oder erst nach dem Commit?

Gruß
Ralf

Re: Wann wird eine Tabelle gesperrt?

27. September 2010 15:50

Direkt danach. Bei Stapeldurchläufen kann es aber sein, daß ein Durchlauf so schnell ausgeführt wird, daß keine andere Aktion dazwischengelassen wird.

Re: Wann wird eine Tabelle gesperrt?

27. September 2010 16:03

HattrickHorst hat geschrieben:Direkt danach. Bei Stapeldurchläufen kann es aber sein, daß ein Durchlauf so schnell ausgeführt wird, daß keine andere Aktion dazwischengelassen wird.


Ahhhh, das erklärt einiges. :-D

Vielen Dank für die Hilfe.

Gruß
Ralf

Re: [Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 16:21

HattrickHorst hat geschrieben:Direkt danach. Bei Stapeldurchläufen kann es aber sein, daß ein Durchlauf so schnell ausgeführt wird, daß keine andere Aktion dazwischengelassen wird.


Das kann so nicht sein, denn wie will man einen Beleg buchen. Dort werden mehrere Tabellen geändert, und wenn ein Fehler auftritt wird alles zurück gedreht. Das geht aber nur, wenn die Transktion noch nicht commitet wurde.

Eine SCHREIB- Transaktion wird mit einem Locktable, insert oder modify gestartet. Beendet wird sie durch ein COMMIT oder implizit durch die Rückgabe der Eingabegewalt an den NAV-Client.

Gruß, Fiddi

Re: [Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 16:35

Hier geht es nicht um Transaktionen, sondern um Tabellensperren. Für Transaktionen gilt sicherlich, was du gesagt hast.

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 16:59

Ja wie nun? nach Modify oder nach Commit?

Den Code lasse ich in einer Codeunit laufen.

Gruß
Ralf

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 18:12

Also, wann eine Tabelle gelockt wird, hängt erst einmal vom Server ab Native oder SQL. Während Native immer Tablelocking macht, agiert der SQL-Server hier etwas differenzierter. Das kann von Record- bis zum Table- Lock gehen, je nachdem, was ihm effizienter erscheint.
Also, wenn ich Datensätze in mehreren Tabellen innerhalb einer Transaktion verändern möchte, z.B. Feldwert= Feldwert +1, und diese Transaktion möchte ich zurückdrehen können, dann muss ich verhindern, das ein weiterer Zugriff meinen geänderten Datensatz oder den Ausgangsdatendatz verändert. Deshalb bedingt jede Schreib- Transaktion ein Lock auf die geänderten Datensätze (SQL) oder Tabellen (Native), damit keine andere Funktion meine geänderten Daten ebenfalls ändert.

Deshalb hat ein Lock sehr wohl was mit SCHREIB- Transaktionen zu tun.
Bei Lese- Operationen sieht das mit dem Lock wieder anders aus. Hier interessiert den Native eigentlich kein Lock, da er immer auf die alten Versionen der Datensätze zugreift, bis deren Schreib- Transaktion commitet wurde. Der SQL-Server hat es hier schwerer, da er von jedem Datensatz nur eine Version hat. Da muss er auch beim Lesen den Datensatz locken.


Gruß, Fiddi

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 20:40

Dem widerspreche ich doch gar nicht. Das hat aber nichts mit dem Problem zu tun. Wie du schon richtig sagst, die Transaktionssicherheit wird über die Versionierung gewährleistet. Das Sperren einer Tabelle bzw. eine Datensatzes findet aber, wenn man es nicht explizit aufruft, nur als Klammer um die Befehle INSERT, MODIFY (auch RENAME) und DELETE inkl. der analogen Massenbefehle statt.

Du kennst doch bestimmt die beiden Fehlermeldungen (sinngemäß):
1. Ein anderer Anwender hat den Datensatz geändert, nachdem er gelesen wurde.
2. Die Tabelle ist gesperrt durch einen anderen Anwender.

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 20:51

Hattrickhorst hat geschrieben:Das Sperren einer Tabelle bzw. eine Datensatzes findet aber, wenn man es nicht explizit aufruft, nur als Klammer um die Befehle INSERT, MODIFY (auch RENAME) und DELETE inkl. der analogen Massenbefehle statt.

Der Insert... Startet den Lock. Dieser Lock muss aber bestehen bleiben, bis der letzte schreibende Befehl einer Transaktion abgeschlossen ist, sonst kann man kein Rollback einer Transaktion durchführen.
Deshalb wird das Lock nicht nur während eines Schreibbefehls ausgeführt (als Klammer), sondern solange bis die Transaktion mit einem COMMIT (oder implizit) bestätigt wurde oder mit ERROR abgebrochen wurde.

Gruß, Fiddi

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 21:29

fiddi hat geschrieben:Der Insert... Startet den Lock. Dieser Lock muss aber bestehen bleiben, bis der letzte schreibende Befehl einer Transaktion abgeschlossen ist, sonst kann man kein Rollback einer Transaktion durchführen.

Das wäre mir neu. Warum sollte das auch so sein? Die Transaktion ist doch durch das DBMS und die Versionierung gesichert. Daher kann man auch immer ein Rollback durchführen.

Wie soll das System deiner Meinung nach folgende Situation so lösen, wie du gesagt hast, also mit Lock am Anfang des ersten Schreibvorganges und Unlock am Ende des letzten?
Code:
IF Rec.FINDSET THEN REPEAT
  IF Rec.FIELD = Wert THEN
    Rec.MODIFY;
UNTIL Rec.NEXT = 0;

Um entscheiden zu können, wann der letzte Schreibvorgang stattfindet, müßte das System ja vorab etwaige verbleibende Datensätze im Set auf die Bedingung prüfen, oder nicht?

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 22:09

Jetzt kommt es auf den Server an.

Die Native wird beim ersten Modify die Tabelle für andere Zugriffe sperren, und am Ende der Codeunit, dann wenn NAV die Kontrolle zurück erhält den Commit ausführen, und damit die Tabelle freigeben.
Der SQL-Server wird beim ersten Modify den aktuellen Record sperren, nach einer Anzahl Datensätzen, wird er ein Page-Lock machen, und irgendwann die ganze Tabelle sperren. Auch hier führt das Ende der Codeunit zum Commit und damit zum Ende des Locks.

Die Versionierung hilft nicht bei konkurierenden Schreibzugriffen, es hilft nur bei parallelen Lesezugriffen während des Schreibens. Wenn du in einem Datensatz einen Wert änderst, z.B. Wert := Wert +1 dann musst du diesen Datensatz gegen Änderung blockieren auch beim Versionierungssystem. Ein weiterer Schreibzugriff auf diesen Datensatz benötigt den neuen geänderten Datensatz, damit er weiter zählen kann. Macht er das aber, könnte man die ursprüngliche Version nicht mehr zurückdrehen :-(. Deshalb muss eine weitere Schreibtransaktion auf die erste Transaktion warten, weil der Datensatz gelockt ist.

Gruß, Fiddi

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 23:27

Das hört sich doch schon anders an. Bis zum Ende der Transaktion für Schreibzugriffe, nicht generell bis zum letzten schreibenden Befehl. Daher ja auch im SQL Isolation Level Serializable.

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

27. September 2010 23:43

Jetzt muss Ralf nur noch mal erklären, was er verstanden hat :-D :-D

Ich hoffe, es ist klar geworden, das ein Lock untrennbar mit einer Transaktion verbunden ist, und das ein einmal gesetzter Lock erst bei einem Commit oder Error wieder aufgehoben wird.

Gruß, Fiddi

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 09:52

Die Argumentation kann ich schon nachvollziehen. Ich hatte aber mal irgendwo gelesen, daß das Lock bei Schreibbefehlen automatisch am Anfang gesetzt und am Ende aufgehoben wird. Nachdem, was du sagst, sollte es aber so sein, daß selbst bei einzelnen Änderungen, also ohne Schleife, das Lock bis zum Transaktionsende anhält. Sonst macht die Erklärung für mich keinen Sinn mehr.
Dort hat jemand damals auch die Frage aufgeworfen, wenn das Lock am Anfang eines Schreibbefehls gesetzt und am Ende wieder aufgehoben würde, dann könnte man es ja gleich weglassen, weil dann danach andere Benutzer den Datensatz wieder bearbeiten könnten, bevor die Transaktion der ersten Änderung zu Ende ist. Das wurde dort so erklärt, daß in der Zeit des Locks die Änderungen zwar nur in den Cache laufen und aber ein Marker gesetzt würde, daß dieser Datensatz in der Bearbeitung steht. Beim Rollback würde dann wieder der Marker entfernt werden. Das Lock sichert dann sozusagen das Setzen des Markers. Hört sich für mich ebenfalls plausibel an. :-?
Definitiv ist es so, daß ein anderer Benutzer die Datensätze zwischendurch lesen kann und wenn er eine Änderung macht, wartet das System (außer man hat es über LOCKTABLE(FALSE) explizit ausgeschaltet). Ist die Transaktion beendet und der geänderte Datensatz war nicht davon betroffen, wird der Änderungsversuch anstandslos übernommen.
Dazu hätte ich dann noch mal eine andere Frage, was wahrscheinlich auch hier das ursprüngliche Problem ist. Warum gibt es dann einige Stapelverarbeitungen, bei denen andere Benutzer nicht mal lesend weiterarbeiten können bzw. das Fenster mit der Info über die Sperre nicht erscheint? Wie ist das zu erklären?

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 10:33

Der Merker ist aber doch auch nichts anderes als ein Lock. Außerdem gibt es unterschiedliche Locks, gegen Schreiben, Lesen, Lesen und schreiben.
Wobei der Native Server eigentlich nur Schreib- Locks benötigt, da er auf Grund seines Versionsprinzips während einer laufenden Schreib- Transaktion immer zwei Versionen eines Datensatzes in der DB hat, den alten und den gerade geänderten. Lesetransaktionen sehen immer nur die alte Versionen, während die Schreibtransaktion ihren Datensatz bearbeitet. Welcher Datensatz aktuell ist, findet der Server anhand der letzten committeten Transaktionsid heraus. Ist die ID des aktuellen Datensatzes niedriger als die des ältesten uncommitteten, dann ist der Datensatz gültig, ansonsten ändert gerade jemand diesen Datensatz. Dieses Datenbankprinzip hat Vorteile bei Anwendungen, die sehr viel lesen und wenig Schreiben. Außerdem benötigt es kein Logfile. Nachteil ist die nötige "Garbage- Collection", die laufen muss um nicht mehr gültige Datensätze als gelöscht zu markieren. Da immer neue Speicherplätze für neue Datensatzversionen gesucht werden müssen, fragmentiert diese Art der Datenbank leichter.

Beim SQL- Server ist das etwas anders, der hat nur einen Datensatz. Alle Änderungen werden im Logfile aufgezeichnet, damit sie zurückgefahren werden können. Hier muss man eigentlich auch beim Lesen ein Lock gegen Schreiben setzen, da ja der Datensatz verändert werden könnte, den man gerade lesen möchte. Das würde aber die Anwendung zu sehr blockieren. Deshalb werden beim SQL-Server auch nicht committete Daten zurückgegeben, es sei denn, man setzt explizit ein Lock. Das lese ich zumindest aus der NAV2009 Hilfe zum Thema "Locking in MS SQL-Server" heraus.

NAV2009 Hilfe hat geschrieben:When data is read without locking, you get the latest (possibly uncommitted) data from the database. If you call Rec.LOCKTABLE, nothing happens. However, when data is read from the table after LOCKTABLE has been called, the data is locked.


Gruß, Fiddi

Re: [Noch nicht Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 10:37

fiddi hat geschrieben:Jetzt muss Ralf nur noch mal erklären, was er verstanden hat :-D :-D

Ich hoffe, es ist klar geworden, das ein Lock untrennbar mit einer Transaktion verbunden ist, und das ein einmal gesetzter Lock erst bei einem Commit oder Error wieder aufgehoben wird.

Gruß, Fiddi


Nun, interessante Diskussion. :-D

Mir ist es nun klar geworden.
Deshalb mache ich jetzt auch nach dem Modify ein Commit und meine sorgen bin ich los.

Vielen Dank. :-D

Gruß Ralf

Re: [Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 10:56

ralf5 hat geschrieben:Mir ist es nun klar geworden.
Deshalb mache ich jetzt auch nach dem Modify ein Commit und meine sorgen bin ich los.


Nun ja :-D
Du soltest noch folgendes beachten:
  • Jedes Commit kostet nicht unerheblich viel Zeit. Deshalb bei großen Datenmengen sparsam damit umgehen
  • Ein Commit sollte nur dann von Hand ins System eingebracht werden, wenn es eine abgeschlossene Gruppe von Befehlen beendet, und der normale implizite Commit nicht ausreicht weil nachfolgender Code die geänderten Daten benötigt. Ein Proforma- Commit kann sehr viel Unheil in der DB anrichten, weil die Daten nicht mehr konsistent sind. Das gilt besonders für Code der aus unterschiedlichen Modulen aufgerufen wird.

Gruß, Fiddi

Re: [Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 11:32

Klar, der Merker signalisiert anderen Usern, daß hier gerade etwas geändert wird. Das ist dann sozusagen ein Lock. Nur wie gesagt, da scheint es ja auch noch mal Unterschiede zu geben. Ich meine, zum einen gibt es ein "Lock" in dem Sinne, daß man mit der Tabelle gar nicht mehr arbeiten kann bzw. alle anderen Aktionen (auch simples Lesen) auf der Tabelle dermaßen langsam ablaufen, daß es dem Benutzer nicht mehr zumutbar ist. Zum anderen gibt es auch ein Lock im eigentlichen Sinne, daß Änderungen vor Transaktionen anderer Benutzer geschützt werden. Beispielsweise wenn man mal einen Excel-Export über den Excel-Buffer auf nicht temporär setzt und da viele Daten eingestellt werden, dann haben es alle anderen Aktionen auf den Excel-Buffer schwer überhaupt noch voranzukommen, obwohl sie eigentlich mit einer temporären Kopie arbeiten sollten. Das verstehe ich dann noch nicht ganz, warum manchmal das System lahmt und manchmal nicht. Ich dachte bisher, das würde mit den beiden o.g. Fehlermeldungen zusammenhängen.

fiddi hat geschrieben:Welcher Datensatz aktuell ist, findet der Server anhand der letzten committeten Transaktionsid heraus. Ist die ID des aktuellen Datensatzes niedriger als die des ältesten uncommitteten, dann ist der Datensatz gültig, ansonsten ändert gerade jemand diesen Datensatz.

Müßte es dann nicht andersrum sein? Oder heißt das, daß alle Datensätze, die von einer Transaktion bearbeitet werden (könnten), vorab eine neue ID bekommen?

Es gibt noch einen anderen Grund, warum das in der Hilfe so steht. Durch explizites Aufrufen kann man ein spätes Lock erreichen.
MSDN hat geschrieben:The purpose of locking with LOCKTABLE(TRUE,TRUE) after reading the records for the first time is to improve concurrency by postponing the table lock that Classic Database Server puts on the table.

Re: [Gelöst] Wann wird eine Tabelle gesperrt?

28. September 2010 11:59

fiddi hat geschrieben:Nun ja :-D
Du soltest noch folgendes beachten:
  • Jedes Commit kostet nicht unerheblich viel Zeit. Deshalb bei großen Datenmengen sparsam damit umgehen
  • Ein Commit sollte nur dann von Hand ins System eingebracht werden, wenn es eine abgeschlossene Gruppe von Befehlen beendet, und der normale implizite Commit nicht ausreicht weil nachfolgender Code die geänderten Daten benötigt. Ein Proforma- Commit kann sehr viel Unheil in der DB anrichten, weil die Daten nicht mehr konsistent sind. Das gilt besonders für Code der aus unterschiedlichen Modulen aufgerufen wird.

Gruß, Fiddi


Normalerweise mach ich auch, aus genau diesen Gründen, kein COMMIT.
In diesem Fall ist das aber die richtige Lösung.

Gruß
Ralf