Ankündigung

Einklappen
Keine Ankündigung bisher.

MySQL - Lock Tables oder nicht?

Einklappen

Neue Werbung 2019

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

  • MySQL - Lock Tables oder nicht?

    [EDIT]: Verständnisfrage: verstehe ich LOCK TABLES so richtig, dass dabei die ganze Tabelle gesperrt wird und die anderen Anfragen hinten anstehen und NICHT weggeworfen werden? Also auch keine Fehlermeldung kommt, weil er es von selbst in ein paar (Micro)Sekunden wieder probiert?

    Hallo zusammen,
    ich habe eine mysql-PHP-Anwendung.
    Die Datenbank ist MySQL 5.6, MyISAM-Tabellen.
    Auf die Anwendung können ca. 100.000 User aufteilt auf 20 Firmen (a max. 12.000 User) gleichzeitig zugreifen.
    Die Anwendung ist ein Formular, in das die Kunden sich einloggen, Ihre Daten eingeben und abschicken.
    Das geht nur 1x pro User - dann ist der Zugang für diesen User gesperrt.

    Jede Firma hat 2 Tabellen. Eine mit den Kundendaten (max. 12.000 Einträge) und eine für die neu eingegebenen Daten der Kunden (max. 4.000 Einträge am Ende).
    Es gibt eine Tabelle, in der ich alle Logins speichere (erfolgreich oder nicht) und die via Update mit der insert-ID aus der Datentabelle versorgt wird.
    Und noch eine Tabelle für die Sessions. Der Session-Handler ist eine PHP-Class aus dem Netz, welche die Sessions auf die MySQL-DB umstellt.

    Jetzt hatte ich bei einer Überprüfung das Problem, dass zwar korrekte Logins in der Login-Tabelle waren (insert-ID=0), aber keine dazugehörigen Dateneinträge in der Daten-Tabelle.
    Mal abgesehen von dem Fall, dass der Kunde sich korrekt einloggt, dann aber das Formular vor dem Absenden verlässt - an was kann es sonst liegen?

    Meine Vermutung geht dahin, dass wohl zwei oder mehrere Zugriffe auf die Datenbank / Tabelle zur gleichen Zeit waren und somit nur einer der Einträge gespeichert wurde.

    1. Wie stelle ich das (im Laufenden Betrieb!) fest? Wie kann ich das nachprüfen, dass das das Problem war?
    2. Wie kann ich es verhindern?
    3. Kann ich MySQL (via PHP) anweisen, dass es bei erfolglosem speichern eine kleine Schleife durchläuft, bis die DB wieder ansprechbar ist?
    4. Hilft mir hier ein Lock Tables? Ich hab das so verstanden, dass es a) nur was bringt, wenn man an mehrere Tabellen gleichzeitig was ändert und was konsistent bleiben muss und b) während eines Locks andere Abfragen ins Leere laufen - wie kann ich das dann abfangen in meiner Anwendung - am besten so, dass es der User nicht mitbekommt und seine Daten dennoch gespeichert werden? (Zwischenspeicher? Schleife?)

    Dazu muss ich noch sagen:
    Der Eintrag in die Datentabelle und das Update der Login-Tabelle geschehen zum gleichen Zeitpunkt (logisch).
    Die Abfragen passieren über PHP mit PDO und (meist) mit prepared Statements.

    Ich habe eine PHP-Datei (als index.php sozusagen), die alles händelt.
    Darin ist auch der Aufruf bzw. die Verbindung zur Datenbank drin.

    Diesen DB-Handler übergebe ich bei allen Funktionen mit, die eine Abfrage an die MySQL-DB senden wollen.

    Vielleicht kann mir jemand weiterhelfen oder den entscheidenden Tipp geben.
    Im Laufenden Betrieb kann ich leider keine größeren Tests oder Änderungen vornehmen, da ja sonst Einträge der Kunden ins Leere laufen.

    Code-Snippets auf Anfrage möglich. Müsst mir nur sagen, welche Stellen genau. Ich kann nur schlecht meinen kompletten Quellcode hier posten.

    Danke schon mal...


  • #2
    Finde erstmal heraus ob das ein technisches Problem ist oder nur das Verhalten deiner Nutzer. Z.B. schau im Access Log des Webservers ob sich die Nutzer wirklich nur Anmelden und dann verschwinden oder doch mehr machen.

    Meine Vermutung geht dahin, dass wohl zwei oder mehrere Zugriffe auf die Datenbank / Tabelle zur gleichen Zeit waren und somit nur einer der Einträge gespeichert wurde.
    Ähm, nein. Mysql ist eine Mehrbenutzersysteme und auf parallele Zugriffe ausgelegt. Was passieren kann sind z.B. Race Conditions. Nach deiner Beschreibung her ist das aber auszuschließen. Ich würde eher auf ein Bug in der Anwendung tippen, oder ebend das Nutzerverhalten.

    PS: es gibt nur noch sehr wenige Gründ MyISAM zu verwenden. InnoDB ist die besser Wahl.

    Kommentar


    • #3
      Gibt es für diese Weblogs eine grafische Oberfläche zum besseren Auslesen? Pro IP kommen a schnell mal über 100 Einträge zusammen.
      Etwas unübersichtlich.

      "Race Conditions" ist ein gutes Stichwort. Die PHP-Class (Zebra-Sessions), die ich hier verwende, schreibt jedoch ausdrücklich, dass es damit klappen würde.
      Evtl. mach ich was verkehrt.
      Div. php-Dateien werden eben auch über AJAX aufgerufen (z.B. der Login), das Speichern der Daten des Formulars jedoch nicht....
      Wo könnte hier dann das Problem liegen?
      Leider habe ich die Problematik noch nicht ganz verstanden. Bin da erst am Anfang.

      Wenn InnoDB besser wäre - (siehe mein anderer Thread) wie verhält sich das dann mit den langsameren Inserts und Updates - was heiß langsamer? 10%, 50%, doppelt so langsam? (bei ca. 3000 Datensätze z.B.)

      Kommentar


      • #4
        InnoDB ist heutzutage nicht mehr langsamer als MyISAM. Den Geschwindigkeitsverlust haben die da praktisch wett gemacht. Rechne mit einigen wenigen Prozent bzw. führe Benchmarks durch, wenn du es genau wissen musst. Achte auf korrekte (und sinnvolle) Indezes, dann kommt es gut.

        Naja, wenn die PHP-Klasse das sagt, muss es ja so sein ... Ich denke, du kannst sehr gut Race-Conditions selber provozieren. Keine Klasse der Welt kann dir saubere Programmierung abnehmen.

        Ein Abruf über AJAX ist das gleiche wie ein normaler HTTP-Request. Der Unterschied besteht nur in der primären Sichtbarkeit für den Benutzer.

        Du wirst dich durchlesen müssen. Suche bestimmte Ausschnitte und analysiere diese, sicher ist nicht das komplette Log interessant. Du hast ja bereits eine Vermutung, wann das Problem auftritt. Analysiere die ersten 100 Zeilen nach diesem Moment.
        GitHub.com - ChrisAndChris - RowMapper und QueryBuilder für MySQL-Datenbanken

        Kommentar


        • #5
          Zitat von dawson77 Beitrag anzeigen
          [EDIT]:

          Jetzt hatte ich bei einer Überprüfung das Problem, dass zwar korrekte Logins in der Login-Tabelle waren (insert-ID=0), aber keine dazugehörigen Dateneinträge in der Daten-Tabelle.
          Mal abgesehen von dem Fall, dass der Kunde sich korrekt einloggt, dann aber das Formular vor dem Absenden verlässt - an was kann es sonst liegen?

          Meine Vermutung geht dahin, dass wohl zwei oder mehrere Zugriffe auf die Datenbank / Tabelle zur gleichen Zeit waren und somit nur einer der Einträge gespeichert wurde.

          1. Wie stelle ich das (im Laufenden Betrieb!) fest? Wie kann ich das nachprüfen, dass das das Problem war?
          2. Wie kann ich es verhindern?
          3. Kann ich MySQL (via PHP) anweisen, dass es bei erfolglosem speichern eine kleine Schleife durchläuft, bis die DB wieder ansprechbar ist?
          4. Hilft mir hier ein Lock Tables? Ich hab das so verstanden, dass es a) nur was bringt, wenn man an mehrere Tabellen gleichzeitig was ändert und was konsistent bleiben muss und b) während eines Locks andere Abfragen ins Leere laufen - wie kann ich das dann abfangen in meiner Anwendung - am besten so, dass es der User nicht mitbekommt und seine Daten dennoch gespeichert werden? (Zwischenspeicher? Schleife?)

          Dazu muss ich noch sagen:
          Der Eintrag in die Datentabelle und das Update der Login-Tabelle geschehen zum gleichen Zeitpunkt (logisch).
          Die Abfragen passieren über PHP mit PDO und (meist) mit prepared Statements.
          So was macht man entweder sehr bequem über writeable Common Table Expressions (wCTE) oder Transaktionen.
          PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

          Kommentar


          • #6
            @akretschmer: writeable Common Table Expressions (wCTE) kannte ich noch nicht. Wird aber (nach kurzer Recherche) nur bei PostgreSQL angewendet. Hab ich nicht im Einsatz. Ergo: keine Lösung.
            Und "Transaktionen": bei PDO würde das wohl so lauten:
            Code:
            $pdo = new PDO(...);
            $pdo -> beginTransaction();
            
            $query = 'UPDATE bla SET blubb = 1 WHERE id < 10';
            $nums  = $pdo -> exec($query);
            
            if (Bedingung) {
              $pdo -> commit();
            }
            else {
              $pdo -> rollBack();
            }
            Ein endTransaction scheints nicht zu geben - die endet dann wohl entweder mit Commit (also mit dem Eintrag) oder dem rollBack (also dem Rückgängig machen der UPDATE-Query).
            Aber nur zum Verständnis: Bei einem Update verstehe ich ja die Transactions. Da will man ggf. nicht von anderen gestört werden um Inkonsistenz zu vermeiden. Aber bei einer normalen Insert? Was soll da passieren? Sehe ich etwas nicht?
            Gut. Mal angenommen, ich binde das so ein...
            a) wäre das alles an Code, was ich ändern muss und
            b) was passiert bei einem rollBack - wie mach ich das dem Besucher klar? Muss er dann z.B. die Daten/das Formular erneut ausfüllen? Setze ich ihn zurück zum Anfang oder wie darf ich mir das vorstellen?

            Kommentar


            • #7
              Bei einem Update verstehe ich ja die Transactions ... Aber bei einer normalen Insert?
              Kann ja auch Aktionen geben, die aus mehreren queries bestehen und "atomar" durchgeführt werden müssen.
              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


              • #8
                Ok. Auch wieder richtig.
                Aber könnt mir einer meine beiden obigen Fragen noch beantworten?
                Ich brauch da noch den einen oder anderen Schubs in die richtige Richtigung - bis dato hab ichs noch nicht 100% geschnallt.

                Kommentar


                • #9
                  Jetzt hab ich gerade das hier im Manual gefunden:
                  Some databases, including MySQL, automatically issue an implicit COMMIT when a database definition language (DDL) statement such as DROP TABLE or CREATE TABLE is issued within a transaction. The implicit COMMIT will prevent you from rolling back any other changes within the transaction boundary.
                  Das betrifft aber jetzt nur DROP, ALTER und CREATE aber nicht INSERT und UPDATE sehe ich das richtig?
                  D.h. eine Transaction mit mehreren INSERTs, SELECTs und UPDATEs klappt.
                  Wenn ein DROP, CREATE oder ALTER dazu kommt, passiert das COMMIT wieder automatisch und der rollBack ist für die Katz, oder?

                  Aber wie gesagt, den rollBack hab ich noch nicht ganz verstanden.
                  Was macht ihr bei einem rollBack in eurer Anwendung? Wie kommuniziert ihr das nach außen? Ich will ja keine ERRORs oder NOTICE aufm Bildschirm haben sondern Usability.

                  Kommentar


                  • #10
                    Zitat von dawson77 Beitrag anzeigen
                    D.h. eine Transaction mit mehreren INSERTs, SELECTs und UPDATEs klappt.
                    Wenn ein DROP, CREATE oder ALTER dazu kommt, passiert das COMMIT wieder automatisch und der rollBack ist für die Katz, oder?
                    Bei primitiven Datenbanken wie MySQL oder Oraggle ja, bei PostgreSQL z.B. nein, da kannst Du auch DDL-Befehle in einer Transaktion machen und auch zurückrollen. Bis auf sehr wenige, dokumentierte Ausnahmen.
                    PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                    Kommentar


                    • #11
                      Naja, wenn man DDL-Statements in einer normalen Transaktion nutzt, dann macht man wahrscheinlich was falsch.
                      Standards - Best Practices - AwesomePHP - Guideline für WebApps

                      Kommentar


                      • #12
                        Zitat von rkr Beitrag anzeigen
                        Naja, wenn man DDL-Statements in einer normalen Transaktion nutzt, dann macht man wahrscheinlich was falsch.
                        Nur weil man etwas nicht kennt muß dieses nicht schlecht/falsch sein. Du hast ein Script, was Dir z.B. die Tabellen via ALTER TABLE erweitert und Daten einspielt, um das Projekt auf eine neue Version zu bringen. Blöd, wenn es mitten drin abbricht, oder?
                        PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

                        Kommentar


                        • #13
                          Ok, ich glaube, ich verstehe den Punkt. Ich bin gedanklich da mehr beim Produktionsbetrieb gewesen, wo die Erstellung oder Anpassung von Tabellen eher nicht stattfinden sollte.

                          Für Migrationsskripte wäre sowas eine prima Sache!
                          Standards - Best Practices - AwesomePHP - Guideline für WebApps

                          Kommentar


                          • #14
                            Zitat von dawson77 Beitrag anzeigen
                            Aber nur zum Verständnis: Bei einem Update verstehe ich ja die Transactions. Da will man ggf. nicht von anderen gestört werden um Inkonsistenz zu vermeiden.
                            Ein Query läuft Atomar ab (myisam teils nicht!), da kann kein anderer Query dazwischen kommen. Bei einer Transaktionen werden mehrere Queries zusammen ausgeführt. Die Datenbank stellt dabei verscheiden Möglichkeiten bereit sicherzustellen das sich Transaktionen nicht in die Quere kommen.

                            Zitat von dawson77 Beitrag anzeigen
                            a) wäre das alles an Code, was ich ändern muss und
                            Was soll diese Änderung bezwecken?

                            Zitat von dawson77 Beitrag anzeigen
                            b) was passiert bei einem rollBack - wie mach ich das dem Besucher klar? Muss er dann z.B. die Daten/das Formular erneut ausfüllen? Setze ich ihn zurück zum Anfang oder wie darf ich mir das vorstellen?
                            Bei einem Rollback werden alle Änderungen der Transaktion rückgänig gemacht. Ein Rollback wird entweder durch ein SQL Fehler ausgelöst, ein Verbindungsabbruch ohne Commit (z.B. Bug im Script) oder manuell. Die ersten beiden Fälle erzeugen in PHP ein Fehler, welche sich am besten per Exception behandeln lassen. Beim manuellen Rollback, wie oben im Code, wird es für das Rollback ein Grund geben und wie du damit umgehst ist deine Sache. In der Regel macht man aber selten selbst ein Rollback, das wird nur da verwendet wo mit externen Ressourcen gearbeitet wird.

                            Kommentar

                            Lädt...
                            X