Anzeige:
Ergebnis 1 bis 13 von 13

Thema: ohne "lock table" auskommen?

  1. #1
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665

    ohne "lock table" auskommen?

    Problemdarstellung:
    ---------------------------------------------------------------------------------------------------------------------------
    Eine MySQL-Tabelle "bla" mit einer Spalte "blubb" und einer Zeile Inhalt (beliebige Zahl für "blubb", z.B. 10)
    ---------------------------------------------------------------------------------------------------------------------------
    PHP-Script führt folgendes aus:

    1. Query: "SELECT `blubb` FROM `bla`"

    Anschließend darf nur fortgefahren werden falls "blubb">0 ist:

    2. Query: "UPDATE `bla` SET `blubb`=`blubb`-'1'"
    ---------------------------------------------------------------------------------------------------------------------------
    Das Problem:

    Wenn das PHP-Script mehrfach parallel läuft kann folgendes passieren:

    Angenommen "blubb" ist 1 und zwei PHP-Scipte führen gleichzeitig Query 1 aus.
    Beide PHP-Scripte führen auch Query 2 aus, da bei beiden "blubb" noch >0 war.

    Das Ergebnis:
    Script 1 setzt "blubb" auf "0", was noch korrekt ist.
    Script 2 setzt "blubb" auf "-1", was eigentlich nicht passieren durfte.
    ---------------------------------------------------------------------------------------------------------------------------
    Wie kann man das nun so lösen, dass auch bei beliebig vielen gleichzeitig laufenden PHP-Scripten "blubb" nie unter 0 fällt?

    Möglicherweise wäre es eine Lösung, die Tabelle "bla" noch vor Query 1 zu sperren und erst nach Query 2 wieder freizugeben.

    Das Problem dabei:
    Die Tabelle wäre komplett blockiert für die Zeit - was wiederum die Performance von anderen PHP-Scripten, die
    einfach nur "blubb" wissen wollen, ohne was zu ändern stark senken würde!?

  2. #2
    Registrierter Benutzer
    Registriert seit
    16.09.2003
    Beiträge
    27
    nimm doch den entsprechenden unsigned datentyp, oder geht das nicht?

  3. #3
    Registrierter Benutzer
    Registriert seit
    05.06.2003
    Beiträge
    118
    Original geschrieben von Maledictus
    nimm doch den entsprechenden unsigned datentyp, oder geht das nicht?
    Ich glaube nicht, daß es um diesen speziellen Fall geht. Das Prinzip ist das Problem.

    Bei mir ist's entweder ein Lock Table oder eine Transaktion. Ich glaube, dies sind die üblichen Lösungsmuster für Verzahnungsprobleme. Anderweitig kann ich dir da nicht helfen.

    AD!

  4. #4
    Registrierter Benutzer Avatar von Gaert
    Registriert seit
    09.05.2002
    Ort
    Nußloch
    Beiträge
    1.317
    Wieso arbeitest du denn nicht mit Transaktionen?

    Teste es einfach mal...

    Du benötigst lediglich eine Transaktionssichere Tabelle (z.B. InnoDB).

    SET AUTOCOMMIT=0;

    START TRANSACTION;
    SELECT `blubb` FROM `bla`
    UPDATE `bla` SET `blubb`=`blubb`-'1'
    COMMIT;
    Siehe auch hier: http://www.mysql.com/doc/en/COMMIT.html

    Und JA (an alle Zweifler) auch MySQL kann Transaktionen!

    Teste doch einfach mal, wie sehr das an der Performance nagt... ich bin sicher weniger als du denkst!


  5. #5
    Registrierter Benutzer Avatar von undefined
    Registriert seit
    01.03.2004
    Beiträge
    1.255

    Re: ohne "lock table" auskommen?

    Original geschrieben von BLUESCREEN3D
    Problemdarstellung:
    Angenommen "blubb" ist 1 und zwei PHP-Scipte führen gleichzeitig Query 1 aus.
    Beide PHP-Scripte führen auch Query 2 aus, da bei beiden "blubb" noch >0 war.

    Das Ergebnis:
    Script 1 setzt "blubb" auf "0", was noch korrekt ist.
    Script 2 setzt "blubb" auf "-1", was eigentlich nicht passieren durfte.
    Wird es auch nicht, weil PHP immer nur einen Prozess an MySQL senden darf bevor der Zweite abgearbeitet wird, an sonsten würde ich wenn du auf nummer Sicher gehen möchtest und Lock nicht zu Verfügung steht register_function() verwenden.
    Siehe einmal in der MySQL Manual unter INSERT [LOW_PRIORITY] oder [IGNORE] nach, dort wird deine Fragestellung ausführlich erleutert

  6. #6
    Registrierter Benutzer Avatar von Gaert
    Registriert seit
    09.05.2002
    Ort
    Nußloch
    Beiträge
    1.317
    @undefined:
    Wo steht, dass PHP nur einen Prozess gleichzeitig bei MySQL abarbeiten darf?
    Klingt für mich unperformant und unlogisch!
    Wir hatten hier schon ähnliche Problemstellungen / schwer reproduzierbare Fehler wie die von Bluescreen3d, die nach unserer meinung darauf zurückzuführen waren, dass mehrere Prozesse auf der Datenbank durcheinandergeschmissen wurden.


  7. #7
    Registrierter Benutzer Avatar von undefined
    Registriert seit
    01.03.2004
    Beiträge
    1.255
    Zugegeben nicht ganz richtig ausgedrückt Wenn du mit PHP an die gleiche Tabelle mehrer Query sendest wird MySQL diese nacheinander abarbeiten. Es sei denn du gibst INSERT DELAYED an dann wird die Warteschleife umgangen und der eintrag ohne Rückfrage eingefügt. Wenn du genau wissen möchtest was gerade geschieht. MySQL Admin extendet-status oder SHOW STATUS.

  8. #8
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665
    Original geschrieben von Thomas Engelke
    Original geschrieben von Maledictus
    nimm doch den entsprechenden unsigned datentyp, oder geht das nicht?
    Ich glaube nicht, daß es um diesen speziellen Fall geht. Das Prinzip ist das Problem.
    jo, am prinzip liegt es ^^

    ------------------------------------------------------------------------------

    Original geschrieben von Gaert
    Wieso arbeitest du denn nicht mit Transaktionen?

    Code:
    SET AUTOCOMMIT=0;
    
    START TRANSACTION;
    SELECT `blubb` FROM `bla`
    UPDATE `bla` SET `blubb`=`blubb`-'1'
    COMMIT;
    dazu habe ich zwei fragen:

    1. unter http://www.mysql.com/doc/en/COMMIT.html steht:
    With START TRANSACTION, autocommit remains disabled until you end the transaction with COMMIT or ROLLBACK. The autocommit mode then reverts to its previous state.
    demnach braucht man in deinem beispiel das "SET AUTOCOMMIT=0;" nicht, oder übersehe ich da was?

    2. was aus dem manual auch nicht ganz hervorgeht ist, ob transaktionen den sinn haben, queries direkt hintereinander auszuführen oder nur eine rückgängigmachung ermöglichen sollen

    ich frage mich nämlich gerade, ob ich das ergebnis des SELECT-queries nicht erst beim COMMIT kriege. dann würde das ja irgendwie nicht gehen, da die ausführung von UPDATE von der SELECT-rückgabe abhängig ist.

    ------------------------------------------------------------------------------

    eigentlich müssen die beiden queries entweder direkt nacheinander ausgeführt werden oder die tabelle darf zwischen den queries nicht geändert werden...

    deshalb bin ich zu folgender idee gekommen, die ich inzwischen für die beste lösung halte (das mit den transaktionen würde ich trotzdem gerne kapieren ):

    nur ein query:
    Code:
    UPDATE `bla` SET `blubb`=`blubb`-'1' WHERE `blubb`>'0'
    ob das ganze erfolgreich war prüfe ich dann mit mysql_affected_rows()

    damit sollte es eigentlich auf keinen fall mehr probleme geben



    EDIT:

    das einzige was so umständlicher ist:

    ich muss im fehlerfall anschließend nochmal blubb abfragen, um eine fehlermeldung anzuzeigen (wenn man davon ausgeht, dass die tabelle mehr spalten als blubb hat, von denen das ganze abhängt)

    dadurch kann die situation entstehen, dass durch einen dritten parallel laufenden prozess der fehler schon nicht mehr vorhanden ist und ich deshalb eine "fehler war vorhanden und ist verschwunden und konnte deshalb nicht lokalisiert werden"-fehlermeldung anzeigen müsste :/
    Geändert von BLUESCREEN3D (29-03-2004 um 17:52 Uhr)

  9. #9
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665
    kaum schreibt man mal etwas mehr kriegt man keine antwort mehr!?

    ist mir hier schon öfter aufgefallen :/

  10. #10
    Registrierter Benutzer Avatar von Gaert
    Registriert seit
    09.05.2002
    Ort
    Nußloch
    Beiträge
    1.317
    Hallo.

    Sorry, für die ausbleibende Antwort - war die letzten drei Tage quasi "ausser Haus".

    Mit dem Autocommit könntest du recht haben, sofern man dem Manual vertraut...

    Zu 2. :
    Eine wichtige Eigenschaft von Transaktionen ist, dass Sie "atomar" ablaufen...
    Die Transaktionen werden gegenüber anderen Transaktionen wie ein Einziger Befehl ausgeführt, ohne dass andere Transaktionen dazwischenpfuschen können.
    Für deine Anwendung Spielt das aber keine Rolle... SELECT Abfrageergebnisse erhälst du sofort, und UPDATES werden auch (gecached) durchgeführt, wodurch du bei späteren SELECTS innerhalb deiner Transaktion wieder die korrekten (neuen) Daten erhälst. Festgeschrieben werden die Daten allerdings erst mit dem Commit - die Transaktionsverwaltung stellt durch eine Sperrverwaltung sicher, dass es nicht zu inkonsistenzen durch parallel laufende Transaktionen kommt.


  11. #11
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665
    Original geschrieben von Gaert
    Eine wichtige Eigenschaft von Transaktionen ist, dass Sie "atomar" ablaufen...
    Die Transaktionen werden gegenüber anderen Transaktionen wie ein Einziger Befehl ausgeführt, ohne dass andere Transaktionen dazwischenpfuschen können.
    Für deine Anwendung Spielt das aber keine Rolle... SELECT Abfrageergebnisse erhälst du sofort
    Genau da liegt das Problem. Da die Ausführung des UPDATE-Queries bei meiner Anwendung von dem Ergebnis des SELECTs abhängt kann das ganze nicht funktionieren, wenn man die Ergebnisse der SELECT-Queries sofort kriegt.

    Ich habe das ganze nochmal mit zwei mysql-Clients an einer InnoDB-Tabelle ausprobiert und den Consoleninhalt nebeneinander gepackt (ich habe die gleichen Befehle immer auf beiden Consolen nacheinander angewendet):

    Code:
    mysql> SET AUTOCOMMIT=0; 1. Befehl			mysql>
    Query OK, 0 rows affected (0.00 sec)			mysql>
    							mysql>
    mysql>							mysql> SET AUTOCOMMIT=0; 2. Befehl
    mysql>							Query OK, 0 rows affected (0.00 sec)
    mysql>
    mysql> START TRANSACTION; 3. Befehl - usw....		mysql>
    Query OK, 0 rows affected (0.00 sec)			mysql>
    							mysql>
    mysql>							mysql> START TRANSACTION;
    mysql>							Query OK, 0 rows affected (0.01 sec)
    mysql>
    mysql> SELECT `blubb` FROM `__bla`;			mysql>
    +-------+						mysql>
    | blubb |						mysql>
    +-------+						mysql>
    |     1 |						mysql>
    +-------+						mysql>
    1 row in set (0.00 sec)					mysql>
    							mysql>
    mysql>							mysql> SELECT `blubb` FROM `__bla`;
    mysql>							+-------+
    mysql>							| blubb |
    mysql>							+-------+
    mysql>							|     1 |
    mysql>							+-------+
    mysql>							1 row in set (0.00 sec)
    mysql>
    mysql> UPDATE `__bla` SET `blubb`=`blubb`-'1';		mysql>
    Query OK, 1 row affected (0.00 sec)			mysql>
    Rows matched: 1  Changed: 1  Warnings: 0		mysql>
     							mysql>
    mysql>							mysql> UPDATE `__bla` SET `blubb`=`blubb`-'1';
    mysql>							Query OK, 1 row affected (11.08 sec) << hier die 11 Sekunden Wartezeit
    mysql>							Rows matched: 1  Changed: 1  Warnings: 0
    mysql>							
    mysql> COMMIT;						mysql>
    Query OK, 0 rows affected (0.00 sec)			mysql>
     							mysql>
    mysql>							mysql> COMMIT;
    mysql>							Query OK, 0 rows affected (0.00 sec)
    mysql>							
    mysql> SELECT `blubb` FROM `__bla`;
    +-------+
    | blubb |
    +-------+
    |    -1 | << und hier hat `blubb` einen negativen Wert, was ja eigentlich gerade verhindert werden sollte
    +-------+
    1 row in set (0.00 sec)
    Wie man an den Zeitangaben sieht kriegte ich das SELECT-Ergebnis wirklich sofort während ich auf das UPDATE-Ergebnis 11 Sekunden warten musste (nämlich bis zum COMMIT in der anderen Console).



    Damit komme ich also um ein "LOCK TABLE `__bla` WRITE" nicht herum :/
    Geändert von BLUESCREEN3D (04-04-2004 um 04:21 Uhr)

  12. #12
    Registrierter Benutzer Avatar von Gaert
    Registriert seit
    09.05.2002
    Ort
    Nußloch
    Beiträge
    1.317
    Hallo Bluescreen,

    und wieder war ich längere Zeit nicht im Forum... Asche auf mein Haupt

    Vielleicht hilft dir ja das hier weiter:
    http://www.mysql.com/doc/en/InnoDB_t...isolation.html


  13. #13
    Registrierter Benutzer Avatar von BLUESCREEN3D
    Registriert seit
    08.11.2002
    Beiträge
    665
    Original geschrieben von Gaert
    Hallo Bluescreen,

    und wieder war ich längere Zeit nicht im Forum... Asche auf mein Haupt
    aber diesmal hatte ich keine antwort erwartet ^^
    ich dachte "lock table" ist die einzige lösung...

    Original geschrieben von Gaert
    Vielleicht hilft dir ja das hier weiter:
    http://www.mysql.com/doc/en/InnoDB_t...isolation.html
    als ich das gelesen habe ist mir aufgefallen, dass ich den transaction isolation level vielleicht nicht ändern muss, sondern aus den SELECT-abfragen einfach nur SELECT ... FOR UPDATE-abfragen machen muss.
    das ganze scheint so zu funktionieren
    also thx für den link

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •