MySQL PDO Datensatz holen und sperren
Bobby
- datenbank
0 Robert R.0 hotti1 1UnitedPower0 Robert R.0 Bobby0 1UnitedPower
0 Christian Kruse
Moin
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten. Wie kann ein Prozess einen Datensatz holen, abarbeiten und danach aus der Datenbank löschen? Wie wäre da der beste Ansatz mit PDO?
Gruß Bobby
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten. Wie kann ein Prozess einen Datensatz holen, abarbeiten und danach aus der Datenbank löschen? Wie wäre da der beste Ansatz mit PDO?
Was hat die Planung des konkurrierenden Betriebes mit PDO zu tun?
Sind die Prozesse eigenständig, also wie es http-Requests üblicherweise sind?
Da hilft für ein MUTEX (gegenseitiger Ausschluss) keine physische Satzsperre.
Helfen können Dir Trigger.
Before Update: prüft, ob sich schon ein Bearbeiter eingetragen hat
und lässt dann keinen weiteren zu
Before Delete: prüft, ob der Löscher mit dem Bearbeiter identisch ist
Auf jeden Fall macht das Eingriffe ins Datenmodell notwendig
Spirituelle Grüße
Euer Robert
Moin
Sind die Prozesse eigenständig, also wie es http-Requests üblicherweise sind?
ja, sind eigenständig und laufen parallel.
Da hilft für ein MUTEX (gegenseitiger Ausschluss) keine physische Satzsperre.
Danke. Das ist genau der Ansatz den ich brauchte.
Helfen können Dir Trigger.
Before Update: prüft, ob sich schon ein Bearbeiter eingetragen hat
und lässt dann keinen weiteren zu
Before Delete: prüft, ob der Löscher mit dem Bearbeiter identisch ist
Interessant
Auf jeden Fall macht das Eingriffe ins Datenmodell notwendig
Das hatte ich vermutet. Was hälst du von einem Lock auf die entsprechende Tabelle während des Lesevorgangs und des Kennzeichnens für "in Bearbeitung"?? der eigentliche Job kann auch danach ausgeführt werden.
Gruß Bobby
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
Das hatte ich vermutet.
Was hälst du von einem Lock auf die entsprechende Tabelle während des Lesevorgangs und des Kennzeichnens für "in Bearbeitung"?? der eigentliche Job kann auch danach ausgeführt werden.
Nichts. Wir reden doch über HTTP, oder? Da funktioniert das nicht!
Ein Vorgang erstreckt sich i.d.R. über mehrere Roundtourns, ein physisches Lock gilt aber nur für einen davon.
Ein DBMS kümmert sich um kurze Locks selber, je nach Maschine darunter auf Tabellenebene (MyISAM) oder auf Satzebene (innoDB). Die beiden Maschinen hier nur als Beispiele.
Es gibt zwei Strategien:
Bei Ungleichheit hat der Bearbeiter die Arschkarte. Seine Arbeit war für die Katz
Alternativ zu den Blockbuffern kann man auch einen Clonflict-Counter (Schreibzähler) führen.
Den sollte man dann durch einen Trigger überwachen lassen. Nur bei Gleichheit zwischen Lesen
und Schreiben wird geschrieben und der Zähler gleichzeitig eins hochgezählt
"Update table set bearbeiter = id_bearbeiter where id = $satzid and id_bearbeiter = 0"
absetzt, kontrolliert, ob das Statement genau einen Satz verändert hat. Ab dann hat man Ruhe.
Außerdem kann jeder andere Bearbeiter die Information abrufen, wer gerade auf den Daten
sitzt.
Dieses Verfahren verhindert i.d.R. eher deadlocks, als das andere, verlangsamt aber auch
meistens die Verwaltungsabläufe
Leider können auf diese Weise Lost Locks entstehen, da bei HTTP ja nicht klar ist, ob der Bearbeiter jemals wiederkommt. Im Web sollte man derartige Sperren daher mindestens noch mit einem Timeout versehen.
Und da sind wir auch schon beim nächsten Konzeptschritt:
Die Verwaltungsinformationen werden nicht mehr direkt in der zu verändernden Tabelle vermerkt, sondern in einer separaten Tabelle, die dann aber bei allen Veränderungen in die Statements mit einbezogen werden muss.
Diese kann man dann beim Logout bereinigen bzw. von Zeit zu Zeit auf abgelaufene Sperren untersuchen. Außerdem kann man nach Belieben Spalten hinzufügen, ohne die eigentlichen Datentabellen damit behelligen zu müssen.
Spirituelle Grüße
Euer Robert
Moin
Was hälst du von einem Lock auf die entsprechende Tabelle während des Lesevorgangs und des Kennzeichnens für "in Bearbeitung"?? der eigentliche Job kann auch danach ausgeführt werden.
Nichts. Wir reden doch über HTTP, oder? Da funktioniert das nicht!
Ein Vorgang erstreckt sich i.d.R. über mehrere Roundtourns, ein physisches Lock gilt aber nur für einen davon.
nene... es sind über exec gestartete PHP-Skripte. Nix mit HTTP. die Locks werden vom selben Script gesetzt und dann wieder gelöst.
Gruß Bobby
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
nene... es sind über exec gestartete PHP-Skripte. Nix mit HTTP. die Locks werden vom selben Script gesetzt und dann wieder gelöst.
Guten Morgen!
Schön, das auch schon zu erfahren.
Allerdings haben die dann auch alle eine eigene PID, auch wenn ihre PPID identisch ist.
Wer setzt denn nun die Locks? Das Parent-Skript?
Spirituelle Grüße
Euer Robert
Moin
Guten Morgen!
Schön, das auch schon zu erfahren.
Hatte ich am Anfang glaub ich mal geschrieben. Die Prozesse laufen im Hintergrund paralell.
Allerdings haben die dann auch alle eine eigene PID, auch wenn ihre PPID identisch ist.
Wer setzt denn nun die Locks? Das Parent-Skript?
Das Lock setzt jeder Prozess allein und entfernt es wieder. Ohne Zutun des Hauptscriptes (aufrufendes Script). Ich denke die PID sind unterschiedlich. wie bekomm ich das eigentlich raus?
Gruß Bobby
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
Guten Morgen!
Schön, das auch schon zu erfahren.Hatte ich am Anfang glaub ich mal geschrieben. Die Prozesse laufen im Hintergrund paralell.
Allerdings haben die dann auch alle eine eigene PID, auch wenn ihre PPID identisch ist.
Wer setzt denn nun die Locks? Das Parent-Skript?Das Lock setzt jeder Prozess allein und entfernt es wieder. Ohne Zutun des Hauptscriptes (aufrufendes Script). Ich denke die PID sind unterschiedlich. wie bekomm ich das eigentlich raus?
Die steht in $!, nachdem der Prozess gesrartet wurde.
http://www.linux-services.org/shell/#SECTION00330000000000000000
musst sie exec() nur ausgeben lassen
$pid = exec("/usr/local/bin/php skript.php > /dev/null & echo $!");
Das Beispiel startet den php-interpreter im Hintergrund, übergibt ihm den Namen des auszuführenden Skripts, lässt alle Standardausgaben im Nirwana verschwinden und führt als zweiten Befel das echo der PID aus. Das $-Zeichen muss hier für den PHP-String maskiert werden.
Spirituelle Grüße
Euer Robert
Moin
Das Beispiel startet den php-interpreter im Hintergrund, übergibt ihm den Namen des auszuführenden Skripts, lässt alle Standardausgaben im Nirwana verschwinden und führt als zweiten Befel das echo der PID aus. Das $-Zeichen muss hier für den PHP-String maskiert werden.
Gut, also alle Prozesse haben eine eigene PID...
Damit sollte das sperren im MySQL doch i.O. sein oder?
Gruß Bobby
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
Das Beispiel startet den php-interpreter im Hintergrund, übergibt ihm den Namen des auszuführenden Skripts, lässt alle Standardausgaben im Nirwana verschwinden und führt als zweiten Befel das echo der PID aus. Das $-Zeichen muss hier für den PHP-String maskiert werden.
Gut, also alle Prozesse haben eine eigene PID...
Damit sollte das sperren im MySQL doch i.O. sein oder?
Das Sperren ist deshalb in diesem Fall ok (*), weil die Prozesse in sich abgeschlossen sind, also sich nicht über mehrere Roundturns erstrecken. Außerdem werden Sperren automatisch aufgelöst, wenn die Datenbankverbindung wieder gelöst wird. Es wird also vermutlich keine Lost-Locks geben.
Bleibt nur noch das Risiko von Deadlocks.
Außerdem musst Du darauf achten, dass die Prozesse auch irgendwann fertig werden, nicht dass Du dir Zombies ranzüchtest.
Spirituelle Grüße
Euer Robert
Moin
Das Sperren ist deshalb in diesem Fall ok (*), weil die Prozesse in sich abgeschlossen sind, also sich nicht über mehrere Roundturns erstrecken. Außerdem werden Sperren automatisch aufgelöst, wenn die Datenbankverbindung wieder gelöst wird. Es wird also vermutlich keine Lost-Locks geben.
Das ist eine gute Nachricht
Bleibt nur noch das Risiko von Deadlocks.
Wie können die entstehen? Wenn ein Prozess einen geblockten DAtensatz findet geht er einfach zum nächsten über.
Außerdem musst Du darauf achten, dass die Prozesse auch irgendwann fertig werden, nicht dass Du dir Zombies ranzüchtest.
Die Prozesse arbeiten nur 1 Job ab. Ablauf ist so: Job aus DB holen (lock, lesen, status setzen, lock lösen) => Daten aus externer Quelle holen => in DB schreiben => Job aus DB löschen => Prozess-Ende => Aufruf neuer Prozess
So zum Ablauf.
Gruß Bobby
Moin
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten.
Mach doch den Prozess selbst oder die kritische Phase atomar, z.B. durch einen Exclusive Lock auf die eigene Datei. Dem OS gelingt es zwar, mehrere Prozesse zu starten, aber vor der kritischen Phase müssen alle Anderen warten, wenn schon einer dran ist. Mit dem Beenden eines Prozesses ist der Lock wieder aufgehoben.
Schöne Grüße.
Hakuna matata!
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten. Wie kann ein Prozess einen Datensatz holen, abarbeiten und danach aus der Datenbank löschen? Wie wäre da der beste Ansatz mit PDO?
Benutz am besten Transaktionen.
Liebe Mitdenker,
liebe Wissende,
liebe Neugierige,
ja!
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten. Wie kann ein Prozess einen Datensatz holen, abarbeiten und danach aus der Datenbank löschen? Wie wäre da der beste Ansatz mit PDO?
Benutz am besten Transaktionen.
Wozu sollte es bei einer linearen, nicht verschachtelten Abarbeitung von Datensätzen notwendig sein, Transaktionen ins Spiel zu bringen? Die fangen erst an sinnvoll zu sein, wenn mehrere Datensätze ggf. mehrerer Tabellen im Spiel sind, die sich gegenseitig in die Quere kommen können.
Leider hat Bobby ja noch nicht rausgelassen, was er da wirklich treibt, aber auf mich macht das den Eindruck eines Massenmailers...
Spirituelle Grüße
Euer Robert
Moin
Leider hat Bobby ja noch nicht rausgelassen, was er da wirklich treibt, aber auf mich macht das den Eindruck eines Massenmailers...
Nein, es ist kein Massenmailer. Keine Angst. Es ist ein Crawler, der Angebote, MIT ERLAUBNIS, von anderen Webseiten nimmt und verarbeitet. Natürlich alles unter den gesetzlichen Urheberrechtsbestimmungen und Kennzeichnungspflichen.
Gruß Bobby
Hakuna matata!
Benutz am besten Transaktionen.
Wozu sollte es bei einer linearen, nicht verschachtelten Abarbeitung von Datensätzen notwendig sein, Transaktionen ins Spiel zu bringen? Die fangen erst an sinnvoll zu sein, wenn mehrere Datensätze ggf. mehrerer Tabellen im Spiel sind, die sich gegenseitig in die Quere kommen können.
Nein, da irrst du dich. Transaktionen sind dann von Interesse, wenn mehrere _Abfragen_ (nicht Datensätze oder Tabellen) voneinander abhängen, insbesondere wenn eine Schreiboperation von einer zuvor ausgeführten Leseoperation abhängt.
Nehmen wir als Beispiel einen Online-Shop: Es gibt Angebot mit limitierter Auflage, solange der Vorrat reicht. Seien nun noch zwei Produkte übrig, und 5 Kunden schicken gleichzeitig ihre Bestellung ab. Das Bestellskript muss prüfen, ob der Artikel noch vorhanden ist, falls dem nicht so ist, muss eine Fehlermeldung an den Kunden gesandt werden, falls doch, dann muss die Bestellung eingetragen werden und der Lagerbestand muss entsprechend verringert werden. Hier liegt eine Racecondition vor, und zwar zwischen dem Auslesen des aktuellen Lagerbestandes und dem Schreiben des neuen Lagerbestandes. Im schlimmsten Fall bekommen alle 5 Kunden eine Bestellbestätigung und das obwohl nur noch zwei Artikel im Lager sind.
Um die Racecondition zu vermeiden, kann man das Auslesen und das Neuschreiben des Lagerbestandes in einer Transaktion abwickeln.
Hakuna matata!
Um die Racecondition zu vermeiden, kann man das Auslesen und das Neuschreiben des Lagerbestandes in einer Transaktion abwickeln.
Das Beispiel ist ein wenig unglücklich gewählt, ich hätte dazu sagen müssen, dass das Auslesen in dem Fall ein Locking Read sein muss.
Moin 1UnitedPower,
Um die Racecondition zu vermeiden, kann man das Auslesen und das Neuschreiben des Lagerbestandes in einer Transaktion abwickeln.
Das Beispiel ist ein wenig unglücklich gewählt, ich hätte dazu sagen müssen, dass das Auslesen in dem Fall ein Locking Read sein muss.
Um das klarzustellen: es muss sogar ein write lock sein, ein read lock reicht nicht. Ich weiss, du hast mit locking reads keinen read lock gemeint - ich wollte es nur klarstellen :)
LG,
CK
Moin Bobby,
ich habe 5 Simultane Prozesse, die sich Daten aus einer Tabelle holen und abarbeiten. Ich möchte dass dabei die Prozesse nicht die selben Daten bearbeiten. Wie kann ein Prozess einen Datensatz holen, abarbeiten und danach aus der Datenbank löschen? Wie wäre da der beste Ansatz mit PDO?
MyISAM kann keinen row level lock. Verwendest du also MyISAM, musst du zwangsläufig die Tabelle locken. Um die Lock Times möglichst kurz zu halten, würde ich an deiner Stelle hier eine zusätzliche Spalte in\_progress BOOLEAN NOT NULL DEFAULT false
oder so anlegen. Dann kannst du die Tabelle locken, Datensätze auslesen bis du einen findest, der noch nicht in Bearbeitung ist, die Spalte auf true
setzen und dann den Lock lösen. Für das DELETE
nach Abschluss des Scrapings brauchst du dann keinen Lock mehr.
Wenn du InnoDB benutzt, reicht es in einer Transaktion einen row level lock zu machen (SELECT ... FOR UPDATE
), die Spalte auf true
zu setzen und dann die Transaktion zu commit
en, da brauchst du den table lock nicht.
LG,
CK