# Datensatzsperrung unter H2 Database



## Alex_winf01 (2. Jan 2008)

Hallo liebe Forengemeinde,

im folgenden Beitrag wurde mir ja schon geholfen: 
Client-Server

Kennt jemand noch eine andere Möglichkeit einen Datensatz in der H2-Datensatz sperren zu lassen, als eine Spalte "inBearbeitung" einzufügen und wenn ein bestimmter DS aufgerufen wird, dieses Feld auf "true" zu setzen? Bei Update und Delete wird diese Spalte abgefragt, ob diese true/false ist. Bei True kann kein Update/Delete durchgeführt werden.


----------



## ms (2. Jan 2008)

Öhm ... in dem verlinkten Thread hab ich Optimistic Locking und Transaktionen schon beschrieben.
Was gefällt dir daran nicht?
Die Variante mit der in-Bearbeitung-Spalte ist eine Krücke und wird nicht wirklich funktionieren.

ms


----------



## Alex_winf01 (2. Jan 2008)

@ ms

ich habe noch nie mit Hibernate gearbeitet. Kannst Du mir dafür ein Beispiel geben?


----------



## Alex_winf01 (2. Jan 2008)

@ ms

Ich habe mal kurz gelesen. Also mit Hibernate wird ja "nur" verglichen, ist der DS wirklich noch der, der er war. Wenn ja, wird er geändert. Wenn nein gibt es eine Fehlermeldung.  D. h. es kann sehr wohl sein, dass ein anderer den entsprechenden DS ändert. Das soll aber gar nicht erst so weit kommen.

Was anderes mache ich mit der Spalte inBearbeitung auch nicht. Wenn diese Spalte true ist, kann ein anderer Anwender gar nicht erst updaten oder löschen. Er bekommt die Fehlermeldung, dass der entsprechende DS gerade in der Bearbeitung ist. OK es ist eine Krücke, aber warum sollte es nicht funktionieren? Welche Probleme können auftreten?


----------



## ms (3. Jan 2008)

Wenn du es so machst wie du sagst, dann musst du doch, bevor du den Datensatz sperrst (also die Spalte InBearbeitung auf TRUE setzt) den Datensatz abfragen ob er nicht gerade durch einen anderen User gesperrt ist. Das sind also zwei Statements die in einer Transaktion laufen sollten. Ansonsten kann es Überschneidungen geben wenn zwei Benutzer gleichzeitig abfragen ob der Datensatz gerade gesperrt ist. Beide bekommen ein FALSE zurück und beide führen dann das Update-Statement aus. Beide Benutzer sind also der Meinung, dass die Daten exklusiv ihnen zur Bearbeitung gehört was natürlich nicht stimmt.
Gut, du könntest natürlich so wie schon erwähnt in dem Update-Statement eine Where-Bedingung anhängen, die auf die Spalte InBearbeitung auf FALSE geht. Dann hast du aber erst wieder eine Art Optimistic Locking eingebaut.
Von gejointen Daten wollen wir erst gar nicht sprechen.

ms


----------



## Alex_winf01 (3. Jan 2008)

> Dann hast du aber erst wieder eine Art Optimistic Locking eingebaut.



Du meinst also, ich bilde das Optimistic Locking nach? Zumindestens im weitesten Sinne. Hintergrund, warum ich das so mache:

Vom Kunden ist es gewollt, dass der DS nicht von einem Anwender bearbeitet werden kann, wenn dieser gerade von einem anderen Anwender bearbeitet wird. Also das Gegenteil, was Hibernate macht. Beispiel: Anwender A öffnet DS A zur Bearbeitung. Anwender B öffnet auch DS A. Anwender A soll den DS bearbeiten könne, Anwender B jedoch nicht. Wenn ich Hibernate richtig verstehe, kann Anwender B sehr wohl den DS A bearbeiten. Anwender A bekommt dann die Rückmeldung, dass der DS geändert wurde und aus dem Grunde nicht gespeichert werden kann.


----------



## FenchelT (3. Jan 2008)

Hallo Alex,

Du solltest Dich nochmal naeher mit datenbankseitigen Transaktionen beschaeftigen.
Denn mitunter ist es auch Aufgabe der Transaktionen, solche Dinge zu vermeiden. Dafuer brauchst Du eigentlich gar keine eigene Locking Methode einfallen lassen,
das kann die DB eigentlich besser und von alleine, sofern die von Dir eingesetzte MySQL Version Transaktionen unterstuetzt.

Gruesse


----------



## Alex_winf01 (3. Jan 2008)

@ FenchelT

Die Varianta mit Hibernate wird vom Kunden nicht gewollt und H2 unterstützt nur Database Locking und nicht für den einzelnen Datensatz.

@ ms

war das so gemeint, wie ich geschrieben habe?


----------



## Guest (3. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> ...und H2 unterstützt nur Database Locking und nicht für den einzelnen Datensatz...


SELECT ... FOR UPDATE macht ein Row-Lock.


----------



## ms (3. Jan 2008)

Anonymous hat gesagt.:
			
		

> SELECT ... FOR UPDATE macht ein Row-Lock.





> It is also possible to lock a *table *exclusively without modifying data, using the statement SELECT ... FOR UPDATE.


Quelle: http://www.h2database.com/h2.pdf (Seite 30, Locking, Lock-Timeout, Deadlocks)
Also kein Row-Lock.

ms


----------



## Alex_winf01 (3. Jan 2008)

@ ms



> Du meinst also, ich bilde das Optimistic Locking nach? Zumindestens im weitesten Sinne.



Leider wurde meine Frage noch nicht beantwortet (vielleicht übersehen?)


----------



## Guest (3. Jan 2008)

ms hat gesagt.:
			
		

> Anonymous hat gesagt.:
> 
> 
> 
> ...


Ok, wo wir schon unterstreichen... 


> It is also possible to lock a *table *exclusively without modifying data, using the statement SELECT ... FOR UPDATE.


Soll heissen, dass es ebenfalls möglich ist eine ganze Tabelle zu sperren. Dies schliesst einen Write-Lock auf Datensatzebene nicht aus.
So verstehe ich es zumindest. Einfach ausprobieren. Aus zwei Connections heraus (autocommit off) ein SELECT...FOR UPDATE auf gleiche 
Datensätze ausführen.
Ich habe hier keine H2-DB auf dem PC, das sollte aber mit zwei Web-Console Fenstern schnell zu testen sein.


----------



## Guest (3. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> @ ms
> 
> 
> 
> ...


Hibernate (bzw. JPA) hat es bereits integriert. Wenn du es von Hand für jede Tabelle machst, wird es ziemlich umständlich. 
Es hängt aber davon ab, was dein Anwendungsszenario ist. "Concurrent updates" zu erkennen oder zu unterbinden. Für's Erste
ist die Lösung OK, beim Zweiten wird es viel komplizierter. In beiden Fällen hast du aber ein Read-Before-Update drin.


----------



## maki (3. Jan 2008)

Dann zitieren wir mal alles und reissen nix aus dem Kontext:


> *Locking, Lock-Timeout, Deadlocks*
> 
> *The database uses table level locks *to give each connection a consistent state of the data. There are two kinds of locks: read locks (shared locks) and write locks (exclusive locks). If a connection wants to reads from a table, and there is no write lock on the table, then a read lock is added to the table. If there is a write lock, then this connection waits for the other connection to release the lock. If connection cannot get a lock for a specified time, then a lock timeout exception is thrown.
> 
> ...


----------



## Alex_winf01 (3. Jan 2008)

Ich habs einfach mal ausprobiert. 


```
set autocommit off
```

1. Fenster: 
	
	
	
	





```
Select ... For Update
```
Ergebnis: Datensatz wird angezeigt

2. Fenster: 
	
	
	
	





```
Select ... For Update
```
Fehlermeldung wegen Zeitüberschreitung


----------



## Guest (3. Jan 2008)

maki hat gesagt.:
			
		

> Dann zitieren wir mal alles und reissen nix aus dem Kontext:


 :toll: 
Warum du dich aber direkt angegriffen fühlst, bleibt mir ein Rätsel.  :roll: 
Ich habe nur die Sätze gesehen, die du hier gepostet hast. Der Teil mit dem "The database uses table level locks..." war nicht dabei...

Wie auch immer... Goosfraba!   

@Alex_winf01
War ja auch klar, die Tabelle ist komplett gesperrt.


----------



## ms (3. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> Fehlermeldung wegen Zeitüberschreitung


Wollte dich gerade darauf hinweisen.

Eine Transaktion bzw. eine Lock sollte immer so kurz wie möglich gehalten werden.
Die default-Timeouts diverser Datenbanken bewegen sich im Millisekunden - Sekundenbereich.
Lies dir nochmal die Stelle mit dem Locking durch, speziell gegen Ende steht genau dass, was dir gerade passiert ist.
Im übrigen hilft dir das Sperren mit SELECT FOR UPDATE nichts, da, wie beschrieben, bei autocommit = true (also ohne Transaktion) sowieso der lock bei jedem statement verloren geht. (Wenn ich es richtig verstanden habe).

Hibernate unterstützt übrigens auch SELECT FOR UPDATE wird aber nicht empfohlen.

Ich bin mir ehrlich gesagt noch nicht sicher was ich an deiner Stelle tun würde.
Es sieht für mich immer mehr nach einer speziellen Anforderung aus die man nicht durch ein Datenbank(standard)feature abdecken kann.
Was du auf jeden Fall brauchst sind Transaktionen (wenn nicht hier dann garantiert irgendwann in deinem Java-RDBMS-Entwickler-Leben)
Verwendest du Hibernate/JPA?
Dein erster Ansatz mit einer eigenen Spalte scheint doch nicht ganz so verkehrt.
Allerdings würde ich eine Spalte mit dem Benutzernamen oder ev. einem eindeutig generierten Token machen.
Der Datensatz bzw. die Datensätze (wenn es mehrere oder gejointe sind) müssen in einer Transaktion dem Benutzer zugeordnet werden. Gleichzeitig müsste es aber auch ein Timeout geben, dass den Datensatz wieder freigibt falls zB der Benutzer beim Bearbeiten einschläft, ...
Wenn der Benutzer in der Spalte steht hat der andere Benutzer gleich auch die Information wer denn gerade diesen Datensatz bearbeitet.

Vielleicht hat ja jemand noch einen besseren Vorschlag.

ms


----------



## ms (3. Jan 2008)

Hier noch was Interessantes aus der H2-Doku:


> Multi-Version Concurrency Control (MVCC)
> The MVCC feature allows higher concurrency than using (table level or row level) locks. When using MVCC in this database, delete, insert and
> update operations will only issue a shared lock on the table. Table are still locked exclusively when adding or removing columns, when dropping
> the table, and when using SELECT ... FOR UPDATE. Connections only 'see' committed data, and own changes. That means, if connection A
> ...


Hilft dem Alex aber auch nicht bei seinem Problem.

ms


----------



## Guest (3. Jan 2008)

Ich würde eine Ad-hoc Lösung bevorzugen. Eine zusätzliche Tabelle, in der der User, die Zieltabelle und die Id(s) der 
Datensätze vermerkt werden, die von einem User "exclusiv ausgecheckt" wurden (PK: Tabellenname + Datensatz-Id). 
Dadurch bleibt das ursprüngliche Schema unangetastet. Alle Schreiboperationen gehen unter Berücksichtigung dieser 
Tabelle.

Der Ablauf könnte wie folgt aussehen

1) SELECT FOR UPDATE für die Lock-Tabelle, auf den Datensatz, den ein Benutzer an sich krallen möchte.
1a) Kein Eintrag in der Lock-Tabelle vorhanden, dann ist der Datensatz noch nicht "ausgecheckt".
Für diesen Fall wird ein solcher angelegt. User + Tabelle + Id des Datensatzes + Datum oder was auch immer.
1b) Eintrag in der Lock-Tabelle vorhanden. Raus mit einer entsprechenden Meldung.

... usw. auch bei Updates. Prüfung, ob Datensatz gesperrt ist und wenn ja, ob vom gleichen Benutzer gesperrt oder
von einem anderen...

Das müsste aber ordentlich getestet werden.


----------



## Alex_winf01 (3. Jan 2008)

Vielen Dank an alle für die hilfreiche Diskussion.  

@ Gast

genauso hatte ich es mir vorgestellt. Das natürlich ausreichend getestet werden muss, ist mir klar.


----------



## Alex_winf01 (5. Jan 2008)

Noch eine Frage:

Was mache ich für den Fall, wenn das Programm abschmiert? Angenommen Anwender A bearbeitet gerade Datensatz A. Datensatz A wird für die anderen Anwender gesperrt. Jetzt schmiert dem Anwender A das Programm ab.


----------



## Guest (6. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> Noch eine Frage:
> 
> Was mache ich für den Fall, wenn das Programm abschmiert? Angenommen Anwender A bearbeitet gerade Datensatz A. Datensatz A wird für die anderen Anwender gesperrt. Jetzt schmiert dem Anwender A das Programm ab.


Dem Server (deiner Serveranwendung) ist sowas piepegal.


----------



## Alex_winf01 (6. Jan 2008)

@ Gust

ich nutze den Server-Modus von H2 und kein RMI (da nicht vom Kunden gewünscht) und das Problem bezog sich mehr auch auf das Problem mit der Datensatzsperrung. Beispiel: In der Tabelle für die Datensatzsperrung steht drin:

Anwener        Datensatz        inBearbeitung
Anwender A   Datensatz 1      TRUE

Jetzt schmiert dem Anwender A das Programm ab. Dann steht da doch in der Spalte "inBearbeitung" true drin. Ich nutze Transaktionen.


----------



## Guest (6. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> @ Gust
> 
> ich nutze den Server-Modus von H2 und kein RMI (da nicht vom Kunden gewünscht) und das Problem bezog sich mehr auch auf das Problem mit der Datensatzsperrung. Beispiel: In der Tabelle für die Datensatzsperrung steht drin:
> 
> ...


Wenn es abstürzt, wird die Transaktion nicht mit commit beendet und damit auch keine Änderung übernommen.


----------



## masta // thomas (7. Jan 2008)

ms hat gesagt.:
			
		

> .... Gleichzeitig müsste es aber auch ein Timeout geben, dass den Datensatz wieder freigibt falls zB der Benutzer beim Bearbeiten einschläft ...


----------



## Alex_winf01 (8. Jan 2008)

Nur noch mal zum Verständnis:

Anwender A zeigt sich Datensatz A an. Anwender B soll sich jetzt Datensatz A noch nicht mal mehr anzeigen lassen. Es kommt eine Fehlermeldung "Dieser Datensatz wird gerade von Anwender A bearbeitet". Damit kein Anzeigen, kein bearbeiten, kein löschen.

Ist der Ansatz dann ok?


----------



## robertpic71 (8. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> Ist der Ansatz dann ok?



Kein Anzeigen finde ich etwas krass.. Aber es ist durchaus ein möglicher Ansatz, wenn auch nicht der empfohlene (man sollte keine Locks lange aufrecht erhalten, siehe auch ms). Aber in dem Fall ist die Alternative - eine logische Sperre (durch Datenfelder) auch nicht besonders schön.

Wir verwenden eigentlich bei uns entweder das optimistiche Locken oder die logische Sperre über eigene Felder. In deinem Fall heißt die Entscheidung pessim.Locken vs. logischer Sperre mit eigenen Feldern

Ich fasse mal kurz Vor- und Nachteile zusammen:

Sperre über Datenbank:
+ keine eigenen Felder notwendig
+ abgeschmierter Client wird irgendwann von der DB zurückgezogen
- kann sich negativ auf die Performance auswirken (Datenbank muss Sperren verwalten)
- auch SELECT muss mit Sperre arbeiten (damit man zur LockException gelangt) - Timeout setzen! 
- langes Timeout --> lange Wartezeite für User, kurzes Timeout: unnötige Fehler bei z.B. Serverüberlastung
- kein teilweises Sperren/Updaten des Datensatzes möglich (eher Problem bei vorhandenen Datenbanken)
- Sperren meistens nicht vom Anwender entsperrbar

logische Sperre:
- eigene Felder notwendig
+ ein paar Felder mehr und man sieht wer den Datensatz sperrt
- wenn ein Client abschmiert, bleibt das Sperrflag stehen...
+ beim Lesen muss nicht gesperrt werden und auch keine Timeouts abgewartet werden
+ Benutzer können Sperren mit Programmhilfe ev. selber auflösen
+ Teilupdates möglich

nicht aufgeführt: optimistische Sperre (wäre wohl Sieger nach Punkten)

Zu den Feldern:
Wir verwenden für die logische Sperren meistens ein Statusfeld, welche nicht nur auf Bearbeitung stehen kann, sondern auch für storniert, fertig, unterbrochen usw. Zusammen mit anderen Feldern (letzter Sachbearbeiter, Timestamp letzte Bearbeitung) kann man bei einer Sperre auch sagen, wer den Datensatz sperrt. In Webanwendungen bzw. neuen Dateien, schreibe ich mir auch gerne die id zur Session als Sperrschalter. Wenn die Session ausläuft, entferne ich die logischen Sperren. Eine (selbst verwaltet) Sessiondatei hat bei Client/Server-Anwendungen seinen Reiz.

verlorene Sperrflags / Sperren
In beiden Fällen ist es wichtig bei allen Abbrüchen, Zurückbuttons usw. die Sperre wieder rückgängig zu machen. Bei der Datenbanksperre ist es etwas einfacher (ROLLBACK), bei der logischen Sperre den alten Status wiederherstellen. In beiden Fällen sind Programmfehler/abstürze (Exceptions!) ein Problem. Bei einem Programmfehler (Statement bleibt offen) kann auch die Datenbank nicht erkennen, dass die Sperre nicht mehr aktiv sein soll.

Auch wenn die Datenbanksperre weniger Arbeit und nach weniger Problemen aussieht, wäre das bei uns kein Option. Wenn man nicht weiß wer den Datensatz sperrt, muss man die Arbeitsplätze "durchgehen". 

Ich persönlich würde mir eine Sessiondatei anlegen. Bei jeder Anmeldung wird eine Sessioneintrag für den Benutzer/IP-Adresse angelegen. Zu sperrende Sätze mit der SessionId (als Sperrschalter) versehen und zum Entsperren wieder gelöscht. Beim Abmelden die (übergebliebene) Sperren der Session entfernen und den Sessionsatz löschen bzw. auf inaktiv setzen. 

Wenn es beim Anmelden mit der Kombination User/Ip-Adresse bereits eine aktive Session gibt, könnte man nach Rückfrage die Aufheben (Problem bei dir: normalerweise ist IP/Benutzer recht eindeutig, bei einem Terminalserver haben alle Benutzer die gleiche IP). Alternativ könnte ein Superuser auch Sessions löschen.


noch zu den Teilupdates: vermeide das Mischen von Stammdaten und Bewegungsdaten. Beispiel: Bei unserem Artikelstamm (Datenbankdesign > 20 Jahre) gibt die die Stammdaten und Felder für die letzte Bestellung und den Gesamtlagerstand usw. Nach aktuellen Sperrmechanismen ist das totaler Mist. Wenn jemand im Artikelstammblatt ist, und der Artikel inzwischen verkauft wird... Da gibt dann die Notwendigkeit einer logischen Sperre (nur für den Stammblattteil). Aber du hast sicher nicht > 1000 Cobolprogramme an der Datenbank hängen ;-) und kannst die DB vernünftig designen.


/Robert


----------



## ms (8. Jan 2008)

@Alex, dein Problem mit dem Aufheben der Sperre ist, dass du immer schreibst, es gibt keinen Applikationsserver der zwischen Datenbank und Client stehen darf. Habe ich dich richtig verstanden? Wenn ja, wer soll denn die Sperre aufheben?
Wenn du es über eigene Felder machst (ich weis eigentlich noch immer nicht wofür du dich jetzt entschieden hast) dann brauchst du jemand oder etwas, der/das die Sperren überwacht und gegebenenfalls wieder freigibt.

ms


----------



## Alex_winf01 (8. Jan 2008)

Habs mit den zusätzlichen Spalten hinbekommen.

Jetzt habe ich noch eine folgende Frage:

In der Dokumentation steht:



> Table Level Locking
> 
> The database allows multiple concurrent connections to the same database. To make sure all connections only see consistent data, table level locking is used by default. This mechanism does not allow high concurrency, but is very fast. Shared locks and exclusive locks are supported. Before reading from a table, the database tries to add a shared lock to the table (this is only possible if there is no exclusive lock on the object by another connection). If the shared lock is added successfully, the table can be read. It is allowed that other connections also have a shared lock on the same object. If a connection wants to write to a table (update or delete a row), an exclusive lock is required. To get the exclusive lock, other connection must not have any locks on the object. After the connection commits, all locks are released. This database keeps all locks in memory.



Nun habe ich da ein kleines Verständnisproblem. 2 Anwender bearbeiten 2 unterschiedliche Datensätze und klicken gleichzeitig auf speichern. Es wird ein update bei jedem Anwender durchgeführt. Beim ersten Anwender klappt das ja vielleicht noch ganz gut, beim zweiten kann es doch aufgrund des Table Level Locking ein Problem geben. Wäre da vielleicht folgendes eine Lösung:



> Lock Timeout
> 
> If a connection cannot get a lock on an object, the connection waits for some amount of time (the lock timeout). During this time, hopefully the connection holding the lock commits and it is then possible to get the lock. If this is not possible because the other connection does not release the lock for some time, the unsuccessful connection will get a lock timeout exception. The lock timeout can be set individually for each connection


----------



## HoaX (8. Jan 2008)

ja, was war das jetzt die frage?


----------



## Alex_winf01 (8. Jan 2008)

Mein Problem ist das Table Level Locking. Bei einer Mehrbenutzeranwendung speichern mehrere Anwender gleichzeitig unterschiedliche Datensätze speichern. Da ist das Sperren der ganzen Tabelle sch....

Das heisst, Anwender A speichert gerade Datensatz A, gleichzeitig speichert Anwender B den Datensatz B. Beim Update für Anwender A wird die Tabelle gesperrt, Anwender B kann nicht mehr speichern. Wie kann ich das verhindern? Ist da  Lock Timeout eine Lösung?

Ich möchte ja, dass mehrere Anwender gleichzeitig in die gleiche Tabelle speichern können (halt unterschiedliche Datensätze).


----------



## maki (8. Jan 2008)

Ein (richtiges) Client Server System wäre wahrscheinlich besser gewesen als einzelne Anwedungen die alle auf eine DB schreiben.

Auch wäre eine bessere DB (eine, die Rowlocking unterstützt) nicht verkehrt.


----------



## Alex_winf01 (8. Jan 2008)

Im Moment muss ich die Gegebenheiten akzeptieren. Weiß jemand Rat auf meine Frage?


----------



## ms (8. Jan 2008)

Kannst du vielleicht kurz zusammenfassen was aus dem bisher gesagten du jetzt wie einsetzt/behandelst?
Eigenes Feld in der Tabelle?
Verwendest du jetzt Transaktionen oder nicht?
Und wenn ja, für welche Zugriffe/Änderungen?
Wofür willst du genau das Locktimeout einsetzen?

ms


----------



## Alex_winf01 (8. Jan 2008)

Also ich nutze den H2-Server Modus, transaktionen für insert, update, delete. Das Problem mit dem Gleichen Datensatz bearbeiten löse ich über zusätzliche Spalten (inBearbeitung, Anwender, Tabelle, seit_wann_in_Bearbeitung).

@ ms

Mein einzigstes Problem ist jetzt nur noch folgendes: Wenn mehrere Anwender gleichzeitig auf speichern gehen und ein update an unterschiedlichen Datensätzen durchführen, wird beim ersten Anwender die Tabelle gesperrt. Siehe mein Posting mit dem Table Level Locking. Ich möchte natürlich, dass alle Anwender ihre Datensätze in die gleiche Tabelle speichern können. Wie kann ich das machen? Ein Timeout für das Lock?


----------



## ms (8. Jan 2008)

Die Transaktion bzw. der Lock existiert doch nur für die Zeit wo du das Update-Statement durchführst ev. auch über mehrere Statements, also sehr sehr kurz. Somit muss halt die zweite Transaktion ev. kurz warten. Das sollte keine Probleme machen wenn du auch im Programm dafür sorgst, dass die Transaktion bzw. der Lock sehr kurz existiert.
Der Standardwert für DEFAULT_LOCK_TIMEOUT beträgt 5sec, das reicht auf jeden Fall.

ms


----------



## Alex_winf01 (8. Jan 2008)

Und genau die 5 Sekunden sind mein Problem. Wenn innerhalb dieser 5 Sekunden ein 2. Anwender auf speichern klickt, wird er eine Fehlermeldung bekommen, dass die Tabelle komplett gesperrt ist.


----------



## ms (8. Jan 2008)

Nimmst du diese Erkenntnis aus einem Test den du gemacht hast?

Es wird nicht die Datenbank für 5 Sekunden gesperrt sondern wenn nach 5 sec der Lock nicht freigegeben wird => also eine andere Transaktion länger als das Timeout benötigt.

Du brauchst nur dafür zu sorgen, dass deine Transaktionen nicht länger wie das angegebene Timeout dauern, dann gibt es keine Probleme.
Natürlich können auch 5sec zuwenig sein, kommt darauf an wieviele Benutzer gleichzeitig wieviele Datensätze in einer Transaktion aktualisieren bzw. wie lange deine Insert-Update-Delete-Statements dauern. Dann kann man dieses Timeout etwas anheben. Das sind aber Maßnahmen die man *eventuell *im Zuge von Lasttests machen kann. Zum *jetzigen *Zeitpunkt wo du von nur 2 Benutzern redest und grundsätzlich mal das Funktionsprinzip realisieren willst ist das *kein *Thema.

Edit:
Das DEFAULT_LOCK_TIMEOUT beträgt nur 1sec und auch das sollte im Normalfall reichen.

ms


----------



## Alex_winf01 (8. Jan 2008)

@ ms

natürlich ist nicht die ganze Datenbank gesperrt, sondern nur die entsprechende Tabelle, auf die der insert, update, delete-Befehl durchgeführt wird.

Ich weiss nicht, ob wir aneinander vorbei reden. Ich gehe etwa von 50 Anwendern aus. Jeder gibt Daten über die GUI ein und drückt auf speichern. Die Schaltfläche speichern ruft bei mir aus der Datei Update.java die entsprechende Methode zum speichern auf. Das Update wird natürlich innerhalb einer Transaktion durchgeführt. 

Nun speichert irgendein 2. Anwender seine Daten aus einem anderen Datensatz und klickt gleichzeitig auf speichern.  Nun habe ich 2 X ein 
	
	
	
	





```
update tabelle set abc = '' where id = ''
```
 mit 2 Transaktionen gleichzeitig.Die tabelle ist aber für einen kurzen Moment aufgrund der 1. Transaktion gesperrt. Der 2. Anwender bekommt dann doch die Fehlermeldung einer Zeitüberschreitung. 

Oder befinde ich mich da auf dem Holzweg?


----------



## robertpic71 (8. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> Mein einzigstes Problem ist jetzt nur noch folgendes: Wenn mehrere Anwender gleichzeitig auf speichern gehen und ein update an unterschiedlichen Datensätzen durchführen, wird beim ersten Anwender die Tabelle gesperrt. Siehe mein Posting mit dem Table Level Locking. Ich möchte natürlich, dass alle Anwender ihre Datensätze in die gleiche Tabelle speichern können. Wie kann ich das machen? Ein Timeout für das Lock?



Ich glaube dir fehlt ein Commit (Autocommit aus?). Wenn du die Daten per Feldwert sperrst, brauchst das Locking ja nicht aufrecht zu erhalten!

möglicher Ablauf:

Benutzer wählt den Datensatz zur Bearbeitung an
1.) einlesen/prüfen
SELECT der Daten 
prüfen ob Sperrschalter sitzt
wenn ja --> Fehlermeldung
wenn nein --> UPDATE Sperrfelder, COMMIT (*) 

Damit gibt es keine Datenbanksperre. Andere Jobs können verzögerungsfrei auf den
Datensatz zugreifen und am Sperrschalter erkennen, von wem er gesperrt ist.

(*) Theoretisch könnte sich der Datensatz zwischen SELECT (ohne Sperre) und UPDATE ändern.
Dies kann z.B. durch eine Where Abfrage auf Sperrschalter = false im Update geprüft werden.

2a.) Wenn der Benutzer die Daten geändert hat
UPDATE, Sperrfelder löschen und COMMIT

2b.) Wenn der Benutzer abbricht, Sperrfelder löschen, COMMIT

Bei diese Lösung gibt es nie Sperren die auf eine Benutzerreaktion warten. Die Sperrdauer sollte sich im Millisekundenbereich bewegen.



			
				maki hat gesagt.:
			
		

> Auch wäre eine bessere DB (eine, die Rowlocking unterstützt) nicht verkehrt.


H2 bietet auch eine alternative Sperrlogik (MVCC) an. Hier bleibt der Satz immer lesbar. Das ist mMn meistens nicht Notwendig (optimitic Locking!) oder so wie hier: logische Sperren über Felder.



			
				maki hat gesagt.:
			
		

> Ein (richtiges) Client Server System wäre wahrscheinlich besser gewesen als einzelne Anwedungen die alle auf eine DB schreiben.


Ich ziehe eine Serverlösung (Programmier- und wartungstechnisch) jeder Server/Client-Anwendung vor. Allerdings habe ich schon einige Lösungen mit Clients und direkter Datenbankanbindung gemacht (die meisten noch vor Java). Das kann genauso problemlos laufen wie Serveranwendungen oder Clients mit Servercalls - ist aber sicher etwas anfälliger.

/Robert


----------



## ms (8. Jan 2008)

Alex_winf01 hat gesagt.:
			
		

> Die tabelle ist aber für einen kurzen Moment aufgrund der 1. Transaktion gesperrt.


Richtig, aber dieser Moment ist so kurz, sodass im Normalfall ein Timeout von wenigen Sekunden ausreicht.



			
				Alex_winf01 hat gesagt.:
			
		

> Der 2. Anwender bekommt dann doch die Fehlermeldung einer Zeitüberschreitung.


Probier es doch bitte endlich aus anstatt hier laufend Mutmaßungen anzustellen.

ms


----------



## Thomas Müller (11. Jan 2008)

Hallo - also H2 unterstützt nur Table Level Locking, und bisher kein Row Level Locking. Das mit FOR UPDATE stimmt schon, das sperrt die ganze Tabelle. Allerdings ist auch Multi-Version Concurrency Control eingebaut (im Moment Beta).


----------



## HoaX (11. Jan 2008)

oha, prominenter besuch 

gibt es einen plan dass auch row level locking unterstützt wird?


----------



## Gast (11. Jan 2008)

Ja, Row Level Locking ist geplant für dieses Jahr, aber Priorität hat im Moment MVCC.


----------

