Weiter Zurück [Inhalt] Online Suche im Handbuch

17.4 Locks

Locks bei Schreibzugriffen auf ganze Tabellen bremsen SQL Datenbanken ungeheuer aus. Daher hat man in guten Datenbanken zusätzlich sowohl Row-Level als auch Column-Level Locks zur Verfügung. Microsoft SQL 6.5 kennt ROW-LEvel Locks übrigens auch nicht.

Wenn zudem wegen den Foreign Keys noch Locks in weiteren Tabellen angelegt werden müssen, dann brechen fast alle Datenbanken völlig ein.

Besonders bemerkbar mach sich dies, wenn zudem noch serverseitige Cursor eingesetzt werden, und jemand zuerst scrollt, bevor er einen Datensatz ändert. In diesem Moment wird ohne Caching im Treiber (ODBC/JDBC Klasse 4) ständig ein Lock auf eine oder mehrere Tabellen gesetzt. Beherrscht die Datenbank noch kein ROW-Level Locking, so muß der Lock auf die Tabelle, bzw. beim Einsatz von Foreign Keys auf mehrere Tabellen gesetzt werden.

Andere User können währenddessen nichts eingeben oder ändern. Problematisch ist aber auch der Fall, wenn die Datenbank ROW-Level oder Column-Level Locks beherrscht, aber die Locks falsch gesetzt werden. In diesem Fall gibt es zuviele Locks in der Datenbank, was die Performance stark herunterdrückt. Ohne ein vernünftiges Transaktionsmanagement, welches Schreibzugriffe verzögert, um sie dann gesammelt in die Datenbank zu schreiben, bei vielen gleichzeitigen INSERT/UPDATES die Datenbank dann stillstehen.

Die Entwickler von MySQL sind daher einen anderen Weg gegangen. Unter UNIX und NT (nicht Windows 95/98) wird jedem Zugriff auf die Datenbank ein Thread zugeordnet (eine Art Unterprogramm).

Es gibt Threads, die Lesen, und Threads, die in die Datenbank scheiben. Wenn nun viele User gleichzeitig die Datenbank auslesen möchten, können sie dies ohne Probleme tun. Wenn nun einige User in die Datenbank schreiben möchten, so werden für jeden gewünschten Schreibzugriff ein exklusiver Thread zugeordnet. Diese Threads erhalten, bis der Schreibzugriff auf die Datenbank abgearbeitet ist, einen exklusiven Lock auf die ganze Tabelle, während alle anderen Schreib-Threads warten müssen. Normalerweise würde dies den Stillstand für alle anderen User bedeuten, bei anderen Datenbanken zumindest.

MySQL besitzt aber ein ausgefeiltes Thread-Handling. Es stellt alle anderen Schreib-Threads solange in einer Art Task-Manger (oder Scheduler, wem dieser Begriff mehr sagt) zurück, bis kein andere Lese-oder Schreib Thread mehr auf die Datenbank zugreift. Wenn also die Datenbank stark gefordert ist (100 Abfrage/Sekunde), dann könnte es passieren, daß ständig Threads lesend zugreifen, und die Schreib-Threads nicht ausgeführt werden.

Darum haben die Entwickler in SQL das Statement LOW_PRORITY bzw. DELAYED bei SELECT, INSERT, UPDATE.. (DELAYED) ..Statements (alle DML Befehle) hinzugefügt, damit einige Treads gegenüber anderen priorisiert, also bevorzugt abgearbeitet werden können. Man kann somit entweder den Lesevorgängen oder den Schreibvorgängen eine hohe Priorität geben. Im Allgemeinen gibt man den Schreibvorgängen eine hohe Priorität vor allen Lesevorgängen, zumal die Schreibvorgänge auch kaum Zeit in Anspruch nehmen, im Vergleich zu den häufiger länger dauernden Abfragen. Wer z.B. INSERT DELAYED benutzt, um Datensätze einzufügen, der wird unter MySQL nicht blockiert, auch wenn die Datenbank unter hoher Last steht. Der Grund ist folgender: Für jeden Schreibzugriff wird ein eigener Thread angelegt, der den INSERT Befehl entgegen nimmt und dem Benutzer OK zurückmeldet. Der Thread wird dann ausgeführt, wenn die Datenbank kurzzeitig ohne Last ist. Das Verfahren ist also ähnlich den von anderen SQL-Datenbanken bekannten Transaktionen, wird nur halt auf Thread-Ebene und auch nur im RAM ausgeführt. Bei einem Absturz sind dann die Änderungen in den noch nicht abgearbeiteten Threads (meist sehr wenige) also verloren.

In der Praxis ist MySQL auch ohne ROW/COLUMN-Locking um den Faktor 5-10 schneller, als z.B. ORACLE, bezogen auf den gesamten Durchsatz. Nach den Erfahrungen beim Aufbau von großen Datenbanken im Internet (Ein Kunde hat 750.000 CD-ROM's zum Verkauf (Siehe http://www.cd-special.de) sind die Antwortzeiten bei MySQL trotz PERL-Frontend sehr kurz. Ein Test mit ORACLE 8i hat im Stresstest ca. Faktor 5-10 längere Antwortzeiten erbracht.

Ein einfacher Pentium mit 128 MB RAM hätte da nicht mehr ausgereicht, sondern es hätte ein DEC-ALPHA Cluster mit LOAD-Balancing zum Einsatz kommen müssen. MySQL kann aber auch Locks auf Tabellen-Ebene ausführen. Siehe hierzu das Kapitel Lock/Unlock von Tabellen. Man kann aber auch ohne Veränderungen des SQL-Codes die Datenbank dazu veranlassen, die Prioritäten anders zu setzen: Hier ein entsprechender Ausschnitt aus der Support-Datenbank der Entwickler von Monty selber geschrieben:

Why not use: 

mysqldump --flush-logs --lock-tables 

set SQL_LOW_PRIORITY_UPDATES=1; 

You can instead start mysqld with: 

--low-priority-updates 

or use: 

[INSERT | UPDATE | DELETE] LOW_PRIORITY ... 

The problem with locks are the following: 

If you do (in this time order) 

#1 LOCK TABLES test READ 
#2 LOCK TABLES test WRITE 
#3 LOCK TABLES test READ 


#1 has a read lock on the table. 
#2 has a 'wait for write lock' on the table 
#3 has a 'wait for read lock' on the table. 

#3 has to wait for #2 as write locks has (normally) higher privilege 
than read locks. 

The reason for this is if you would allow SELECT's to proceed on 
tables where there is a 'waiting write lock', then if you issue new 
selects the whole time, it's likely that there will never be a time when 
the table is 'free' and the clients that wants to update will starve 
to death. 

In the case of INSERT's, you can probably avoid this by using 'INSERT
DELAYED ...' 

Regards, 
Monty


Weiter Zurück [Inhalt] Online Suche im Handbuch