2013-01-08 7 views
9

Ich habe eine MySQL-Update-Abfrage, die manchmal alle Felder aktualisiert und manchmal aktualisiert alle Felder außer einem.Update-Abfrage aktualisiert manchmal Feld in MySQL-Datenbank nicht

Es ist auf etwa 10% der Anrufe fehlgeschlagen.

Mein Tisch ist:

CREATE TABLE IF NOT EXISTS `grades` ( 
`id` int(11) NOT NULL AUTO_INCREMENT, 
`state` int(1) NOT NULL, 
`result` varchar(255) NOT NULL, 
`date_synced` datetime NOT NULL, 
`updated_at` datetime NOT NULL, 
PRIMARY KEY (`id`)) 
ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=4395 ; 

Meine Frage ist:

$sqlstr = "UPDATE grades SET result = '$result', state = 2, date_synced = '$date', updated_at = '$date' WHERE id = $id"; 

Wenn es fehlschlägt, Ergebnis date_synced und updated_at aktualisiert aber Zustand bleibt unverändert.

Es gibt eine weitere Abfrage, die nur das Statusfeld aktualisiert und die auch zeitweise fehlschlägt.

Ich konnte das Problem in unserer Testumgebung nicht neu erstellen. Könnte etwas mit der mySQL-Produktionsdatenbank oder einer Art von Kollisionsverriegelung nicht stimmen?


Ich habe weitere Informationen. Ich benutze mysqli. Die andere Abfrage, die nur den Status aktualisiert, verwendet mysql. Würde das ein Problem verursachen?

Ich dachte InnoDB von Zeile gesperrt. Es erlaubt keine partiellen Zeilenaktualisierungen, oder?


Ein weiteres Update, um die Kommentare zu adressieren.

Mein Code-Flow ist ziemlich linear.

The row is created with state=0. 
<flash stuff here> and the row is updated with state=1 
A cron job pulls all state=1 and sends an api call 
if api call is successful, the row is updated with state=2, result, date_synced, and updated_at 
if api call is error, the row is updated with state=3, result, and updated_at 

Das Statusfeld wird nie wieder auf 0 (nach dem Blitz) oder 1 (nach dem API-Aufruf) eingestellt. Da das Datum _synced und das Ergebnis gesetzt sind, aber (manchmal) der Zustand immernoch 1 ist, ist es so, als ob die Aktualisierung auf das Statusfeld gelöscht wird.

Ich werde den Update-Trigger hinzufügen und sehen, ob das mir mehr Informationen gibt.

+3

sollten Sie einen Aktualisierungstrigger und eine Protokolltabelle hinzufügen, die die alten und neuen Werte zusammen mit der ID, der Sitzungs-ID und dem aktuellen Benutzer zum Debuggen speichert. Vielleicht sind die Änderungen bestanden, wurden aber von einer anderen Anweisung überschrieben. –

+1

Ich denke, Sir Rufos Kommentar könnte für Ihre Situation sehr hilfreich sein, weil er hilft, die Reihenfolge der Dinge zu bestimmen und ob es ein komplexeres Szenario ist, in dem mehrere Abfragen resultieren auf dem Bild, das du siehst. – DWright

+1

"[..] oder eine Art von Kollisionssperrung [..]", da andere Felder in derselben Zeile aktualisiert wurden, haben Sie kein Sperrproblem. Das kleinste, was innodb sperren kann, ist eine Zeile. Ich kann kein einzelnes Feld sperren. – scones

Antwort

1

Gibt es an, dass "{n} Zeilen betroffen?"

Auch ist es wiederholbar; Können Sie die gleiche Abfrage auf genau die gleichen Daten ausführen und es wird verschiedene Dinge tun?

Wenn ja, könnten Sie eine beschädigte Installation oder eine beschädigte Datenbank haben.

Haben Sie versucht, eine Reparatur & auf den Tischen zu optimieren? Das könnte helfen.

Sorry über die Schrot Antwort: P

0

nachjustieren Ihre "Strenge" in MySQL zu TRADITIONAL:

SET sql_mode = 'TRADITIONAL'; 

und den Einsatz aus der Konsole ohne PHP versuchen. Dann nach Fehlern suchen, die es erklären könnten.

die Strikt Zurücksetzen auf „nachsichtig“ von:

SET sql_mode = ''; 

Oder wenn Ihr mit einem PDO Objekt in der Datenbank zu verbinden, einen Fehler zurück durch PHP mit so etwas wie dieses:

try { 
    $sqlstr = "UPDATE grades SET result = '$result', state = 2, date_synced = '$date',updated_at = '$date' WHERE id = $id"; 
$s = $pdo->exec($sqlstr); 
} 
catch (PDOException $e) { 
    $error = $e->getMessage(); 
    echo $error; 
} 

Mach im Grunde alles, was nötig ist, um die Fehlermeldung zu bekommen, was vor sich geht. Es ist sehr wahrscheinlich, dass SQL einen Fehler sendet, aber PHP ist nicht dafür eingerichtet, es in Ihrem Skript anzuzeigen.

+0

Eigentlich ist es wahrscheinlich, dass MySQL eine Warnung generiert, weil der INT (1) abgeschnitten wurde oder so. Also könnte PHP die Warnung einfach ignorieren und fortfahren. –

+1

FWIW, INT (1) und INT (1000000) sind für Speicher und den Bereich der unterstützten Werte identisch. Das Argument ist keine Längenbeschränkung, es ist ein Anzeighinweis. MySQL INT ist immer ein 32-Bit-Datentyp. –

0

Versuchen Sie, den Namen des Felds von "state" in etwas anderes zu ändern, z. B. "state_num". Obwohl es nicht als Reservierungswort für MySQL aufgeführt ist, kann sein Name Probleme verursachen.

0

Ihre Abfrage, die "Status" und andere Felder festlegen, wird zweimal ausgeführt, und manchmal wird eine Aufgabe schneller abgeschlossen als eine andere. Es aktualisiert alle Felder und das Skript, das diese Aufgabe aufgerufen hat, führt auch eine MySQL-Abfrage aus, die das Feld "state" aktualisiert.

Zum Beispiel haben Sie ein PHP-Skript, das einen Shell-Befehl "tar" im Hintergrund über exec() ausführt und nach Abschluss "DB" in DB setzt. Die nächste Zeichenfolge Ihres PHP-Codes aktualisiert Ihren "Status" in Ihrer Datenbank. Aber manchmal läuft der von Ihnen aufgerufene bash-Befehl schneller als Ihr PHP-Skript MySQL update aufruft (wenn dieser bash-Befehl im Hintergrundmodus läuft).

Erster Schritt: lief Bash-Skript von PHP, die „state“ = 3 (wartet auf weitere Verarbeitung) nach dem vollständigen, zu aktualisieren anderen Feldern mit Datum festgelegt usw.

Nächster Schritt: in Ihrem PHP nach exec() Sie setzen "state" = 2 (in Bearbeitung). In diesem Fall, wenn Ihr Bash-Skript im Hintergrund die Aufgabe schneller erledigt als PHP die MySQL-Update-Abfrage ausführt, erhalten Sie den aktualisierten Datumswert und auch "state" = 2 (aber es hatte vor einer Sekunde den Wert = 3).

Ich hatte dieses Problem.

Verwandte Themen