Gleichzeitiger Zugriff auf der DB

deemon84

Mitglied
Hallo,

ich arbeite an einer DB-Applikation und dabei ist das folgende Problem zu lösen.
2 Benutzer (A, B) haben gleichzeitig die Applikation gestartet; nach etwas gesucht und die App zeigt (vereinfacht) folgende Daten an:
Name Qty
---- ---

apple 10
orange 20


Benutzer A ändert 10 auf 15. Nach ca. 1 Minute möchte Benutzer B ebenfalls 10 verändern, jedoch hat B die Suche noch nicht aktualisiert, somit sieht er noch 10.


Frage 1) wie könnte man erreichen, dass in diesem Fall B die Daten (10) nur nach einem Refresh ändern kann? Um die Änderung zu machen verwende ich einen insert-Befehl in einem MS-SQL Server.
Sollte ich bei jedem insert zuerst vergleichen, ob sich die Daten sich inzwischen verändert haben?
Eventuell die DB-Tabellen für bestimmte Zeit sperren, während A damit arbeitet?


Frage 2) Wäre es sinvoll die Tabelle (mit den Suchergebnissen) periodisch zu aktualisieren, damit A und B immer die aktuellste Daten sehen?


Danke,
Daniel
 
Zu 1: Nennt sich Optimistic Locking, gäbe im Prinzip 2 Möglichkeiten:
Du führst ein zusätzliches Version-Attribute ein, bei jedem Insert prüfst du erst auf Gleichheit, und incrementierst es dann.
Oder beim ändern werden alle alten Daten mitgeschickt, im Insert wird dann Tabelle mit allen alten Daten verglichen, und bei Gleichheit die neuen Daten geändert.

Zu 2: Nutzerfreundlich wäre das sicherlich 😉
 
Im Endeffekt gibt es aber keine 100% Lösung für das Problem. Du kannst nur versuchen es zu optimieren.

Vor allem wenn du noch Undo dazu programmierst wird es richtig witzig. Stell Dir mal vor User 1 ändert etwas, dann ändert user 2 etwas. User 1 weiß nun nichts davon und macht einen Undo. Nun wird der eigentlich richtige Wert von User 2 wieder gelöscht und es erscheint der alte Wert von User 1. Ganz übel.

Die Lösung mit dem TimeStamp für jedes Attribut ist schon recht gut aber es verlangsamt die Sache natürlich auch ungemein. Du must ja nicht nur pro Datensatz einen TimeStamp haben sondern pro Spalte in der Tabelle. Es kann ja durchauch sein, dass User 1 den Vornamen ändert und User 2 die Strasse. Im optimalen Fall wird beim Ändern der Strasse nun nur die Strasse geupdated und nicht der vorname wieder zurück gesetzt.

Gruß

Claus
 
Vor allem wenn du noch Undo dazu programmierst wird es richtig witzig. Stell Dir mal vor User 1 ändert etwas, dann ändert user 2 etwas. User 1 weiß nun nichts davon und macht einen Undo. Nun wird der eigentlich richtige Wert von User 2 wieder gelöscht und es erscheint der alte Wert von User 1. Ganz übel.

Mit Version-Attribute zB hätte man das Problem nicht. Angenommen Änderung von User 1 setzt version auf 1, Änderung von User 2 auf 2. Und von User 1 geht dann von Version 1 aus, und schlägt fehl, weils nicht die aktuelle ist.

Die Lösung mit dem TimeStamp für jedes Attribut ist schon recht gut aber es verlangsamt die Sache natürlich auch ungemein. Du must ja nicht nur pro Datensatz einen TimeStamp haben sondern pro Spalte in der Tabelle. Es kann ja durchauch sein, dass User 1 den Vornamen ändert und User 2 die Strasse. Im optimalen Fall wird beim Ändern der Strasse nun nur die Strasse geupdated und nicht der vorname wieder zurück gesetzt.

Es reicht eine Version/Timestamp pro Tabelle. Die Änderungen von User 2 gehen von invaliden Daten aus, also sind die Änderungen nicht möglich, wäre zumindest für mich gewünschtes Verhalten.
Angenommen Name und Adresse stehen in der DB, passen aber nicht zueinander. User 1 ändert Name so, das Name zur Adresse passt, sind dann also korrekt, User 2 ändert gleichzeitig Adresse, das Adresse zum Namen passt. Ginge das beides durch, ständen falsche Daten in der Datenbank.
 
Es reicht eine Version/Timestamp pro Tabelle. Die Änderungen von User 2 gehen von invaliden Daten aus, also sind die Änderungen nicht möglich, wäre zumindest für mich gewünschtes Verhalten.
Angenommen Name und Adresse stehen in der DB, passen aber nicht zueinander. User 1 ändert Name so, das Name zur Adresse passt, sind dann also korrekt, User 2 ändert gleichzeitig Adresse, das Adresse zum Namen passt. Ginge das beides durch, ständen falsche Daten in der Datenbank.

Das meinte ich mit "Es gibt keine 100% Lösung". Zu jedem Beispiel das Du nennst warum du es so machen willst, kann ich wieder ein Gegenbeispiel bringen warum es anders bessser wäre 🙂

Gruß

Claus
 
Danke für Eure Antworten.
Wie löst man 1) in anderen ERP Systemen? Ist es überhaupt realistisch, dass soetwas vorkommt?

https://help.sap.com/saphelp_nw70/helpdata/de/41/7af4c5a79e11d1950f0000e82de14a/content.htm

Bei dieser Frage wird das SAP-Sperrkonzept für dich interessant sein.

TL;DR: Es gibt eine extra Tabelle die nur dafür gedacht ist Sperren zu verwalten.

und ja, es ist sehr realistisch, dass diseser Fall auftritt. Vorallem, wenn man auf einem System mit meheren hundert Leuten arbeitet und Sperren über mehrere Dialoge und Workflows hinweg erhalten bleiben müssen.
 
Zuletzt bearbeitet:
https://help.sap.com/saphelp_nw70/helpdata/de/41/7af4c5a79e11d1950f0000e82de14a/content.htm

Bei dieser Frage wird das SAP-Sperrkonzept für dich interessant sein.

TL;DR: Es gibt eine extra Tabelle die nur dafür gedacht ist Sperren zu verwalten.

und ja, es ist sehr realistisch, dass diseser Fall auftritt. Vorallem, wenn man auf einem System mit meheren hundert Leuten arbeitet und Sperren über mehrere Dialoge und Workflows hinweg erhalten bleiben müssen.

Danke für den Tipp. Dieser scheint recht interessant zu sein. Wahrscheinlich ziemlich aufwendig zu implementieren...
 
Ich habe noch eine Frage bzg. Implementierung. Ich möchte folgendes erreichen:
Code:
QUERY_1_BEGIN
  DECLARE @oldsyncid INTEGER
  SET @oldsyncid = 5  --oldsyncid wird im Java-Code gesetzt
  DECLARE @syncid INTEGER
  SET @syncid = SELECT SYNCID FROM MYTABLE WHERE AWNR=3
 
 
  IF @oldsyncid <> @syncid
  BEGIN
   THROW 51000, 'Daten wurden inzwischen verändert!', 1;
   END
  ELSE
  BEGIN
   UPDATE MYTABLE WITH(ROWLOCK) SET SYNCID = @syncid+1  WHERE AWNR=3
   -- Rest of the Transaction
   END
QUERY_1_END


Sobald "SET @syncid = SELECT ..." ausgeführt wird, möchte ich die entsprechende DB-Zeile sperren, damit solange QUERY_1 läuft, niemand darauf zugreifen kann (weder Lesen, noch Schreiben).
Ich meine, wenn QUERY_2 und QUERY_3 ... abgearbeitet werden, sollen diese auf QUERY_1 warten. (ähnlich, wie ein synchronized{} - Blokk in Java)
Ich habe gelesen, dass beim UPDATE ein ROWLOCK gibt (http://stackoverflow.com/questions/3114826/is-it-possible-to-force-row-level-locking-in-sql-server). Aber ich weiß nicht, ob man soetwas im SQL machen kann.
Die ganze Tabelle möchte ich auch nicht sperren, wenn's möglich ist.
Hat jemand Erfahrung damit?

Danke für Eure Hilfe,
Daniel
 

Zurück
Oben