Frage

Es kommt manchmal vor, dass zwei Administratoren in unserem Support-Team versuchen, dieselbe vertrauliche Operation für die DB-Tabellenzeile auszuführen (z. B. den Wert in der Zeile ändern). Das müssen wir verhindern. (Das Sperren von Zeilen ist nicht möglich, da Tabellen "myisam" sind.)

Ich habe an mehrere Lösungen gedacht:

Setzen Sie den alten Wert im Formular und vergleichen Sie ihn mit dem aktuellen Wert beim Senden
   <input name="money"><input type="hidden" name="old_money" value="10">

und dann vor dem Aktualisieren:

   $currentmoney=value_from_query("select money from mytable","money");
   if($currentmoney!=$_REQUEST["old_money"]){
      return "value changed to $currentmoney while you were editing it, are you sure you still want to change it?!??!?!?!?";
   }
   else{
     mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'");
     return true;
   }

aber es kann folgende Situation geben:

  1. Benutzer muss den Geldwert von 9 $ auf 10 $ ändern.

  2. admin1 ändert sein Geld in 10 $

  3. Benutzer gibt geschickt 1 $ aus, sodass sein aktuelles Geld wieder 9 $ beträgt!

  4. admin2 ändert sein Geld ohne Vorwarnung auf 10 $.

    Erstellen der Einstellung für den Zeitstempel (Spalte "update_at") in der Zeile

    Und das Gleiche wie in Lösung 1. Dies hat den Vorteil, dass es mehr sagt als nur einen einfachen Datenvergleich. Wir können mit Sicherheit sagen, ob Daten geändert wurden, während wir mit dem Formular herumgespielt haben oder nicht. Nachteil - Wir können nicht verfolgen, welche Spalte genau geändert wurde, es sei denn, wir kombinieren sie mit Lösung 1

       <input type="hidden" name="formtimestamp" value="<? echo time();?>">
    

    und dann beim Aktualisieren:

       $query_add = ($overriden ? "" : " and updated_at>'".securevalue($_REQUEST["formtimestamp"])."'");
       if(mysql_affected_rows(mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."', updated_at=NOW() where user='$user_id' ".$query_add))==0){
          return "some values were changed by someone else while you were editing it, are you sure you still want to change it?!??!?!?!?";
       }
       else{
         return true;
       }
    

    Erstellen der temporären Datei mit der Länge 0 mit dem objekt- / aktionsspezifischen Namen

    Erstellen / Sperren während des Updates und Überprüfen auf Existenz / Datenstempel vor dem Update.

    Vor dem Update:

       $myfname="/tmp/user{$user_id}EDITMONEY.tmp";
       $timedifference=((time()-filectime($myfname)); //in seconds
       if(file_exists($myfname) and ($timedifference<60) and (!$overriden)){ // a minute difference
          $currentmoney=value_from_query("select money from mytable","money");
          return "money were edited by someone else $timedifference seconds ago and set to {$currentmoney}, are you sure you still want to change it?!??!?!?!?";
       }else{
          $fp = fopen("/tmp/user".intval($_REQUEST["user_id"])."EDITMONEY.tmp", "r+");         
          if (flock($fp, LOCK_EX)) { // do an exclusive lock
             mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'")
            flock($fp, LOCK_UN); // release the lock
            return true;
         } else {
            return "Couldn't get the lock, it's possible that someone tried to execute query simultaneously!";
         }
    
       fclose($fp);
    
       }
    

    Im Moment ist die Dateierstellung mein bevorzugter Ansatz, weil:

    1. Ich denke, es ist schneller, eine lokale Datei zu erstellen als auf eine Datenbank zuzugreifen.

    2. Ich muss der Tabelle keine weitere Spalte (Zeitstempel) hinzufügen

    3. Ich kann den Dateinamen leicht ändern, um nach bestimmten Spaltenänderungen zu suchen, dh die Datei "money_user {$ userid} _modified" erstellen, wenn mysqlupdate fertig ist.

      Ist das richtig oder gibt es etwas, das ich falsch verstehe?

War es hilfreich?

Lösung

Sie können den alten Wert in der UPDATE-Klausel der WHERE-Operation angeben und dann die Anzahl der betroffenen Zeilen überprüfen:

Gegeben

id  name          amount
--- ------------- ---------
1   Joe User      10

Thread 1 wird ausgeführt

UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 1 row(s) affected

Thread 2 wird ausgeführt

UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 0 row(s) affected

Abgesehen davon würde ich den Ausschluss wahrscheinlich etwas früher implementieren, indem ich zuerst einzelnen Administratoren Aufgaben zuweise, um die Zeitverschwendung zu reduzieren.

Andere Tipps

In Ihrem Fall ist das Sperren vermutlich der beste Ansatz.Sie können MySQL-Sperren verwenden: GET_LOCK, RELEASE_LOCK, IS_FREE_LOCK.Transaktionen garantieren meiner Meinung nach nicht, dass sich die Zeile nicht ändert, während ein anderer Prozess seine Aufgabe für abgerufene Daten ausführt.

Obwohl Ihr spezieller Fall nichts mit dem Sperren im herkömmlichen Sinne zu tun hat.IMHO müssen Sie Ihre Transaktionen mit entsprechenden Anmeldeinformationen und Beschreibungen protokollieren, damit Ihre Administratoren sie lesen können und nicht dieselbe Änderung des Kontostands überspielen.Die Sperre kann vor gleichzeitigen Zeilenänderungen schützen, jedoch nicht vor absichtlichen Änderungen beim Überspielen.

Ich denke, dass das Sperren auf Datenbankebene der Datenbank nicht die Situation erfüllt, die Sie in der ersten Methode erwähnt haben.Aber ich denke auch nicht, dass die Dateierstellung schneller ist als der Zugriff auf das Datenbanksystem.Die Dateierstellung ist offensichtlich schwerer als CRUD in der Datenbank.

Daher schlage ich einen ähnlichen Ansatz für die Protokollierungstabelle vor.

  • Jede Tabelle hat einen eigenen Primärschlüssel (wie pid)
  • Notieren Sie den Tabellennamen und die PID mit dem Zeitstempel in der Protokolltabelle, wenn jemand versucht, eine Zeile zu fummeln.
  • Überprüfen Sie die Protokolltabelle, bevor Sie eine Abfrage ausführen.

Schauen Sie sich InnoDB und Transaktionen an.Sie eignen sich besser für empfindliche Veränderungen (dh Gleichgewicht).

Datenbanken sind im Allgemeinen besser, da sie eine zentralisierte Lösung darstellen.Wenn Sie aufgrund von Datenverkehr oder allgemeiner Arbeitsbelastung skalieren müssen, ist es nicht einfach, diese Dateien zu synchronisieren.Es ist in Ordnung, wenn Sie nicht erwarten, dass eine Skalierung erforderlich ist und die E / A-Raten gut sind.

Gestatten Sie mir, zwei mögliche Lösungen zu nennen, die Sie möglicherweise auch oben erwähnt haben.

Sie können eine "zugewiesene_ID" mit der ID Ihres Administratorkontos in Kombination mit einem Zeitstempel hinzufügen, damit Ihre Anwendung eine Warnung anzeigt, wenn jemand anderes sie bearbeitet.

Eine andere mögliche Lösung besteht darin, zu überprüfen, ob beim Ausfüllen Ihrer Formulare Änderungen vorgenommen wurden.Hier könnte ein zuletzt bearbeiteter Zeitstempel verwendet werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top