Re: Performance-Problem beim SCHLIESSEN einer Datei - Die LDB ist schuld !
- From: "Peter Götz" <gssg_nospam@xxxxxxxxxxx>
- Date: Tue, 21 Oct 2008 11:01:48 +0200
Hallo Wolfgang,
ich bin ja ungern einer der seine Mitmenschen nerven
möchte, aber wg. der immer wieder auftretenden
Performance-Probleme von DAO im
Netzwerk/Mehrbenutzerproblem habe ich heute
eine für mich erstaunliche Entdeckung gemacht:
Performance-Verluste bei gleichzeitig geöffneten
Dateien von mehreren Benutzern entstehen beim
SCHLIESSEN (Close) einer Datei und nicht beim
Öffnen (OPEN)
Welche "gleichzeitig geöffneten Dateien"?
Ich rate mal, Du meinst eine Datei (*.mdb), die
gleichzeitig von mehreren Benutzern genutzt
wird.
Wie das ?
Die Jet-Engine (das beschränkt sich nicht auf DAO,
sondern gilt auch für ADO und ADO.net) erstellt eine
*.ldb, wenn die erste Connection zur zugehörigen *.mdb
hergestellt wird und trägt in diese *.ldb den Rechner-
und den Benutzernamen ein. Wird eine weitere Connection
zur *.mdb geöffnet, schreibt die Jet-Engine in die nun bereits
vorhandene *.ldb einen weiteren Eintrag mit Rechner-
und Benutzername.
Wird eine Connection zur *.mdb geschlossen, löscht die
Jet-Engine den zugehörigen Eintrag (Rechner- u. Benutzer-
name) aus der *.ldb. Ist der zu löschende Eintrag der
letzte und einzig noch verbliebene Eintrag, so löscht die
Jet-Engine die gesamte *.ldb.
So weit, so einfach.
Das macht schon mal deutlich, dass das Öffnen und
Schliessen der Connection sowohl ein evtl. genutztes
LAN als auch den Server, auf welchem *.mdb und *.ldb
liegen, belasten. Dabei wird also Rechenzeit verbraten
und Server und Netzwerklast produziert.
Bekanntermaßen legt DAO beim Öffnen einer MDB
automatisch eine gleichlautende LDB-Datei für
BenutzerInfo-Satzsperren und wasweisich an.
Beim Schließen der MDB wird automatisch die
LDB-Datei wieder gelöscht sofern dies der einzigste
Zugriff von allen Anwendungen/Benutzer war.
Ja, genau so ist es.
Wobei Anwendungen/Benutzer nicht ganz richtig ist.
Eine Anwendung kann auch mehr als ein DataBase-
bzw. Connection-Objekt zur *.mdb erstellen und für
jedes davon wird ein Eintrag in der *.ldb erstellt.
Und genau hier liegt die Krux.
Wenn die MDB geschlossen wird und die aktuelle Anwendung
NICHT der einzigste Zugriff war, dann wird die LDB eben
nicht gelöscht.
Natürlich nicht, denn sonst wüssten ja andere Clients
nicht mehr, dass die *.mdb eben auch noch von weiteren
Clients (Mehrbenutzerbetrieb) genutzt wird. Wenn Du
keine *.ldb haben willst, dann musst Du die *.mdb
im Exklusiv-Modus öffnen. Eine solche Sperre ist dann
in der *.mdb selbst vermerkt, eine *.ldb ist dabei
nicht notwendig und auch nicht vorhanden.
Das kann DAO aber erst nach einigen "Lösch-Probier-
Fehlversuchen" feststellen und gibt dann auf.
Welche Lösch- u. Probierversuche?
Es ist für die Jet-Engine doch relativ einfach, festzustellen,
ob es in der *.ldb nur einen oder mehrere Benutzereinträge
gibt. Dazu braucht es keine Lösch- und/oder Probierversuche.
Durch diese "Lösch-Probier-Fehlversuchen" geht mir
aber im Anwendungs-Verhalten wertvolle Zeit verloren,
die meine Benutzer sichtbar nervt.
Nicht durch irgendwelches Probieren, sondern schlicht und
einfach durch die Zeit, welche benötigt wird, den Benutzer-
eintrag aus der *.ldb zu löschen und im Falle des letzen
Eintrages auch noch die ganze *.ldb zu löschen.
Am Zeitverhalten der Ausführung von "dbsNorthwind.Close"
kann ich das ganz deutlich messen.
Ja, bei jedem DB.Close und/oder bei den moderneren Varianten
ADO und ADO.net bei jedem Connection.Close wird
Rechenzeit für das Löschen eines Eintrages aus der *.ldb und/
oder das Löschen der gesamten *.ldb benötigt. Das bedeutet
Rechenzeit beim Client selbst und im Falle einer *.mdb/*.ldb
auf einem entfernten Rechner eben auch noch Rechenzeit
auf dem entfernten Rechner (z.B. Datei-Server).
Genau deshalb sollte man während der Programmlaufzeit eines
Clientprogramms, welches in dieser Zeit wiederholt auf die *.mdb
zugreift, nicht ständig die Verbindung zur DB öffnen und wieder
schliessen, sondern besser beim Programmstart einmalig öffnen
und beim Programmende wieder schliessen. Für alle DB-
Zugriffe wird dann während der gesamten Programmlaufzeit
das einzige DataBase- bzw. Connection-Objekt genutzt.
Es muss dann nicht für jeden Zugriff erneut die Verbindung
geöffnet (DB.Open bzw. Connection.Open) und nach dem
Abschluss des Zugriffs die Verbindung wieder geschlossen
werden, was ja jedesmal auch einen Zugriff auf die *.ldb
nach sich ziehen würde.
Ist die Datei von nur einer Anwendung geöffnet, dann ist
das Schließen praktisch zeitlos,
Na ja, so ganz zeitlos dann doch nicht.
Es dauert schon auch etwas, wenn die *.ldb gelöscht
werden muss. Wie lange das dauert, hängt nicht zuletzt
auch von dem Rechner ab, auf dem die *.ldb liegt.
hat noch eine andere Anwendung im Netzwerk diese Datei
geöffnet dauert das Schließen schon mal 2 Sek.
2 Sekunden kann ich so nicht bestätigen. Da dürfte wohl der
Rechner, auf welchem die *.mdb und *.ldb liegen sehr stark
mit anderen Dingen beschäftigt sein.
Eine nervend lange Zeit.
Die, selbst wenn es 2 Sekunden wären, wenig Bedeutung
hätten, wenn das Schliessen der Datenbank ohnehin nur
einmalig, eben beim Beenden des Programmes ausgeführt
wird.
Den einzigsten Hinweis auf diese Phänomen finde ich unter
http://www.granite.ab.ca/access/performanceldblocking.htm
Na ja, in diesem Artikel werden aber ein wenig Äpfel mit
Birnen verglichen. Wenn ein geöffnetes Recordset erst
dann geschlossen wird, wenn das OnClose der DB oder
bei ADO/ADO.net das Close-Ereignis der Connection
ausgelöst wird, dann kostet natürlich das Schliessen
des Recordsets auch eine nicht zu vernachlässigende
Zeit, das um so mehr, wenn an dieses Recordset auch
noch irgendwelche Controls gebunden sind.
Die hier vorgeschlagenen Lösung, die Datei mit einer
globalen Variablen ständig offen zu halten finde ich wenig
praktikabel.
Warum sollte das nicht praktikabel sein.
Die DataBase resp. Connection wird beim Programm-
start geöffnet, alle DB-Zugriffe werden über dieses eine
DataBase- resp. Connectionobjekt abgewickelt und beim
Programmende wird DataBase bzw. Connection wieder
geschlossen. Das vermeidet ständige Änderungen an der
zur *.mdb gehörenden *.ldb, schont so das LAN und den
Server auf dem *.mdb und *.ldb liegen.
Hier wollte hier nun mal fragen ob der Sachverhalt bekannt,
Ja, natürlich ist der bekannt, seit es die Jet-Engine gibt.
und ggfs. noch andere Abhilfemöglichkeiten bekannt
sind,
Ja, s.oben:
DataBase bzw. Connection beim Programmstart öffnen
und erst beim Programmende wieder schliessen. Dies
bedeutet einen einzigen Zugriff auf die *.ldb beim Öffnen
und einen weiteren beim Schliessen von DataBase bzw.
Connection. Die dazwischenliegenden DB-Zugriffe erfordern
keinerlei Änderungen an der *.ldb. Mehrfaches Öffnen und
gleich wieder Schliessen wäre reine Ressourcen-
verschwendung und würde lediglich das LAN und den
Server unnötig belasten.
z.B. das Löschen der LDB ganz zu
unterbinden,
Das wäre nicht praktikabel, da in diesem Fall
die *.mdb z.B. nie mehr exklusiv geöffnet werden
könnte.
den Timeout zu verkleinern etc.
Welchen Timeout?
Ein für den Mehrbenutzerbetrieb konzipiertes Programm,
welches via Jet-Engine auf eine *.mdb zugreift und das
die Connection beim Programmstart öffnet, sowie erst
beim Programmende wieder schliesst, findest Du unter
www.gssg.de -> VisualBasic -> VBclassic
-> Datenbank -> ADODemoMu2002
Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
.
- References:
- DAO: Performance-Problem beim SCHLIESSEN einer Datei - Die LDB ist schuld !
- From: Wolfgang Schwarz
- DAO: Performance-Problem beim SCHLIESSEN einer Datei - Die LDB ist schuld !
- Prev by Date: Re: Performance-Problem beim SCHLIESSEN einer Datei - Die LDB ist schuld !
- Next by Date: Massig Daten in DB mit teils existierenden Einträgen importieren
- Previous by thread: Re: Performance-Problem beim SCHLIESSEN einer Datei - Die LDB ist schuld !
- Next by thread: Massig Daten in DB mit teils existierenden Einträgen importieren
- Index(es):
Relevant Pages
|