Bobby: MySQL PDO Datensatz holen und sperren

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

--
-> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
### Henry L. Mencken ###
-> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
### Viktor Frankl ###
ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
  1. 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

    --
    Möge der Forumsgeist wiederbelebt werden!
    1. 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

      --
      -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
      ### Henry L. Mencken ###
      -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
      ### Viktor Frankl ###
      ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
      1. 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:

        • "Happy Forward" oder optimistic Locking
            gesperrt wird erst zur tatsächlichen Veränderung
            dabei wird gesperrt, der alte Satz nochmal gelesen, mit dem im alten Blockbuffer verglichen,
            und bei Gleichkeit der alten Buffer wird der neue geschrieben.

        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

        • "Scaredy cat" oder pessimistic Locking
            Der Datensatz wird vor dem _Bearbeiten_ im Offline-Buffer für den Bearbeiter gesperrt.
            Das geht ganz einfach, indem man ein

        "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

        --
        Möge der Forumsgeist wiederbelebt werden!
        1. 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

          --
          -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
          ### Henry L. Mencken ###
          -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
          ### Viktor Frankl ###
          ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
          1. 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

            --
            Möge der Forumsgeist wiederbelebt werden!
            1. 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

              --
              -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
              ### Henry L. Mencken ###
              -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
              ### Viktor Frankl ###
              ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
              1. 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

                --
                Möge der Forumsgeist wiederbelebt werden!
                1. 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

                  --
                  -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
                  ### Henry L. Mencken ###
                  -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
                  ### Viktor Frankl ###
                  ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
                  1. 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

                    --
                    Möge der Forumsgeist wiederbelebt werden!
                    1. 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

                      --
                      -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
                      ### Henry L. Mencken ###
                      -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
                      ### Viktor Frankl ###
                      ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
  2. 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.

    --
    Das Beste am Norden ist, da wo ich gestern abend war (Ooch Thiel, Du altes Ferkel *G*).
  3. 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.

    --
    “All right, then, I'll go to hell.” – Huck Finn
    1. 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

      --
      Möge der Forumsgeist wiederbelebt werden!
      1. 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

        --
        -> Für jedes Problem gibt es eine Lösung, die einfach, sauber und falsch ist! <-
        ### Henry L. Mencken ###
        -> Nicht das Problem macht die Schwierigkeiten, sondern unsere Sichtweise! <-
        ### Viktor Frankl ###
        ie:{ br:> fl:{ va:} ls:< fo:) rl:( n4:( de:> ss:) ch:? js:( mo:} sh:) zu:)
      2. 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.

        --
        “All right, then, I'll go to hell.” – Huck Finn
        1. 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.

          --
          “All right, then, I'll go to hell.” – Huck Finn
          1. 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

  4. 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 commiten, da brauchst du den table lock nicht.

    LG,
     CK