Ankündigung

Einklappen
Keine Ankündigung bisher.

PHP / PDO / MYSQL doppelte ausführung

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • PHP / PDO / MYSQL doppelte ausführung

    Hallo,

    ich hab da ein kleines Problem, wo ich auf dem Schlauch stehe.
    In einer Datenbank habe ich einen Tabelle Benutzer mit Feld „Guthaben“.
    Dann habe ich eine 2. Tabelle Name: gesperrt mit Feld Guthaben und Status.

    wenn in der zweiten Tabelle
    20 | 0

    steht, soll diese 20 übertragen werden auf Benutzer-Tabelle auf das Guthaben. Status wird auf 1 gesetzt.

    Also der Ablauf:
    1. Lade Eintrag aus Tabelle gesperrt und erhalte 20 | 0
    2. Nehme 20 addiere auf Benutzer guthaben
    3. Stelle Status auf 1

    wenn ich jetzt die Seite ganz schnell 2x aufrufe passiert folgendes:
    1. Lade Eintrag aus Tabelle gesperrt und erhalte 20 | 0
    2. Nehme 20 addiere auf Benutzer guthaben
    3. Lade Eintrag aus Tabelle gesperrt und erhalte 20 | 0
    4. aktualisiere Feld Status auf 1
    5. Nehme 20 addiere auf Benutzer guthaben
    6. Aktualisiere Feld Status auf 1

    selbst wenn ich Status auf 1 direkt als erstes mache kann dies ja passieren. Wie verhindert man sowas am einfachsten?

    danke!

  • #2
    Zeig bitte mal, wie Du es genau machst, anhand Deines Code. Dann können wir Dir Optimierungs-Hinweise geben oder auf Fehler aufmerksam machen.
    So ist die Frage reine Theorie.
    Competence-Center -> Enjoy the Informatrix
    PHProcks!Einsteiger freundliche TutorialsPreComposed Packages

    Kommentar


    • #3
      Liest sich wie ein klassischer Fall für Transaktionen auf der Datenbank.

      Kommentar


      • #4
        MOD: Verschoben von PHP-Fortgeschritten
        The string "()()" is not palindrom but the String "())(" is.

        Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
        PHP.de Wissenssammlung | Kein Support per PN

        Kommentar


        • #5
          Zitat von Tropi Beitrag anzeigen
          Liest sich wie ein klassischer Fall für Transaktionen auf der Datenbank.
          Eine Transaktionen alleine reicht hier nicht. Es muss noch ein entsprechender Lock gesetzt werden:

          https://dev.mysql.com/doc/refman/5.7...ing-reads.html

          In dem Fall SELECT ... FOR UPDATE für gesperrt

          Kommentar


          • #6
            Hallo,

            vielen dank für die zahlreichen Antworten.
            beginTransaction hatte ich testeweise schon Integriert, da es auf einigen Seiten echt Sinn macht. Mir war nur nicht klar wie das behilflich sein kann.

            Das Zauberwort scheint in der Tat „FOR UPDATE“ zu sein.

            Habe mir 2 kleine Dateien gebastelt die einfach in einer Test-Tabelle einen Wert erhöhen. In der ersten Datei habe ich ein sleep(20); hinzugefügt um sie Transaction offen zu halten während die 2te Datei geladen wird.

            Wenn ich in der ersten Datei FOR UPDATE reinnehme, dann die zweite lade, lädt diese ewig lang. Und zwar bis die 20 Sekunden der ersten abgelaufen ist. Entferne ich das FOR UPDATE, kann ich zweitere Datei trotz sleep der ersten beliebig oft ausführen.

            Verstehe ich den Zusammenhang damit also richtig, dass MySQL Keine exception wirft „Eintrag gesperrt“, sondern definitiv so lange wartet bis FOR UPDATE möglich ist? Sprich bis die Sperre aufgehoben ist?

            danke!!

            Kommentar


            • #7
              Zitat von Stephan18 Beitrag anzeigen
              Verstehe ich den Zusammenhang damit also richtig, dass MySQL Keine exception wirft „Eintrag gesperrt“, sondern definitiv so lange wartet bis FOR UPDATE möglich ist? Sprich bis die Sperre aufgehoben ist?
              Mysql wartet nur eine bestimmte Zeit und bricht den Query dann ggf. ab. Das sind In der Regel 50 Sekunden und kann über innodb_lock_wait_timeout geändert werden.

              Kommentar


              • #8
                Zitat von Stephan18 Beitrag anzeigen
                Verstehe ich den Zusammenhang damit also richtig, dass MySQL Keine exception wirft „Eintrag gesperrt“, sondern definitiv so lange wartet bis FOR UPDATE möglich ist? Sprich bis die Sperre aufgehoben ist?
                Stell dir das Szenario doch mal im Echtbetrieb mit hohen Datenaukommen vor. Was meinst du? Gegebenfalls tausende Exceptions pro Tag oder eine DB die wartet bis der Satz wieder frei ist weil eine Aktion halt mal etwas länger dauert. Dafür gibt es Timeouts, falls es zu lange dauert.
                The string "()()" is not palindrom but the String "())(" is.

                Debugging: Finde DEINE Fehler selbst! | Gegen Probleme beim E-Mail-Versand | Sicheres Passwort-Hashing | Includes niemals ohne __DIR__
                PHP.de Wissenssammlung | Kein Support per PN

                Kommentar


                • #9
                  Zitat von Stephan18 Beitrag anzeigen
                  Entferne ich das FOR UPDATE, kann ich zweitere Datei trotz sleep der ersten beliebig oft ausführen.
                  Du sollst auch nicht das FOR UPDATE entfernen, sondern dein Sleep. Das verzögert das Problem einfach nur um 20 Sekunden und bringt ansonsten nichts.

                  All locks set by LOCK IN SHARE MODE and FOR UPDATE queries are released when the transaction is committed or rolled back
                  D.h. du kannst den Lock nach deinem SELECT + UPDATE sofort wieder freigeben. Dann ist es egal wie lange der Rest der Seite lädt (z.B. 20s wegen sleep()).

                  Kommentar


                  • #10
                    Zitat von Tropi Beitrag anzeigen

                    Du sollst auch nicht das FOR UPDATE entfernen, sondern dein Sleep. Das verzögert das Problem einfach nur um 20 Sekunden und bringt ansonsten nichts.
                    Hallo,

                    doch es bringt etwas. Ich habe das ja nur zum Testen von FOR UPDATE gemacht. Um zu sehen, was passiert wenn der zweite Query kommt. Ob eine exception kommt, ich also das Handling selber übernehmen muss oder ob MySQL die Timeouts verwaltet.
                    selbstverständlich ist in einem richtigen Skript, was nicht zum testen via „learning by doing“ vorhanden ist, absolut tabu. Gibt keine sleeps. Keine sleeps in keinem Programm.

                    deshalb. Es war nur zum testen damit ich 20sekunden Zeit habe die nächste Datei aufzurufen und eben genau diese Problematik aus dem eingangspost simulieren kann und dafür 20sekunden habe!

                    danke allen für die Hilfe. Habt mir sehr geholfen

                    Kommentar


                    • #11
                      Okay, dann habe ich dich falsch verstanden. Dachte du möchtest mit dem sleep() das Problem lösen.
                      Hoffe du hast's jetzt hinbekommen.

                      Kommentar


                      • #12
                        Nein nein

                        das sleep würde ja nichts ändern. Wenn ich dann halt die zweite datei später Aufrufe hab ich es ja wieder. Reine Test-Funktion.

                        und ja ich hab das alles anhand 2 Test-Dateien simuliert und zu dem Ergebnis gekommen dass genau das die Lösung ist.

                        ich habe wirklich ungelogen Tagelang nach „Datensätze sperren“ und ähnlichem gesucht. Dieses FOR UPDATE habe ich entweder unbewusst überlesen oder es nicht gefunden. So simple und dennoch effektiv - klasse

                        Kommentar

                        Lädt...
                        X