2017-09-28 3 views
0

Ich habe eine MySQL-Tabelle, die automatisch einmal pro Tag aktualisiert wird. Das Skript, das dieses Update ausführt, funktioniert wie der folgende PHP-Code. Was es grundsätzlich macht, erstellt eine temporäre Tabelle mit der gleichen Struktur, fügt zuerst alle Daten dort ein, schneidet dann die tatsächliche Tabelle ab und fügt die Daten aus der temporären Tabelle ein. schließlich wird der temporäre Tisch zerstört. Diese Methode wird verwendet, da sie die Ausfallzeit der tatsächlichen Tabelle erheblich verringert.Basistabelle oder Ansicht nicht gefunden - obwohl es sollte

Das funktionierte für eine ganze Weile gut. Bis vor kurzem mit dem Debian-Upgrade (von Debian 8 auf Debian 9, was auch den Wechsel von MySQL zu MariaDB bedeutet). Jetzt finde ich häufig diese Fehlermeldung in den Protokolldateien:

[Wed Sep 27 06:03:04.903652 2017] [:error] [pid 27393] [client 127.0.0.1:36794] PHP Fatal error: Uncaught PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database.table_temp' doesn't exist in /someFile.php:50\nStack trace:\n#0 /someFile.php(50): PDOStatement->execute()\n#1 {main}\n thrown in /someFile.php on line 50 

Dieser Fehler letztlich bewirkt, dass einige der Daten zu fehlen. Wenn ich es richtig verstehe, heißt es, dass der temporäre Tisch zerstört wurde, bevor die Beilagen fertig waren, oder? Wie kann das passieren, wenn man bedenkt, dass dies nicht wirklich ein TEMPORARY TABLE ist, verwende ich einfach eine echte Tabelle und nenne sie temp. Verursacht MariaDB INSERTs verzögert, so dass das PHP-Skript den DROP ausführen kann, bevor die INSERTs ausgeführt werden?

Dies ist der cron.d Eintrag, so wird das Skript wirklich nur einmal pro Tag ausgeführt:

1 6 * * * root  /usr/bin/wget -q http://www.example.com/someFile.php -O /dev/null 

Es ist auch erwähnenswert ist, dass das Skript innerhalb einer Stunde der Ausführung beendet oder zwei, so dass die Ausführung von Der vorherige Tag ist lange beendet und stört nicht.

Dies ist im Grunde der Code:

$dataArray = []; // much data 

$db->query("DROP TABLE IF EXISTS `table_temp`;"); 
$db->query("CREATE TABLE `table_temp` LIKE `table`;"); 

$tres = $db->prepare(" 
    INSERT INTO `table_temp` 
    (`field1`, `field2`) 
    VALUES 
    (:field1, :field2) 
"); 
foreach($dataArray as $data){ 
    $db->beginTransaction(); 
    $res->execute(); 
    foreach($data as $item){ 
     $tres->bindParam('field1', $item['field1'], PDO::PARAM_INT); 
     $tres->bindParam('field2', $item['field2']); 
     $tres->execute(); // line 50 
    } 
    $db->commit(); 
} 

$db->query("RENAME TABLE `table` TO `table_old`, `table_temp` TO `table`"); 
$db->query("DROP TABLE `table_old`"); 
+0

ist es fast der Fehler, der Ihnen sagt, dass die Tabelle 'table_temp' nicht vor der Vorbereitungsfunktion erstellt wurde ... das ist seltsam, weil PHP auf' $ db-> query ("CREATE TABLE' table_temp' LIKE '" warten sollte) Tabelle ";"); 'hat ausgeführt, das ist normal .. –

+0

Was ist Zeile 50 in Ihrem Code? –

+0

Zeile 50 ist im Code kommentiert. Es ist die Ausführung der Einfügeabfrage – user2015253

Antwort

0

Tropfen; Erstellen; (wahrscheinlich eine Sperre)

Befehle, die durch eine Sperre verzögert werden, werden einer "Warteschlange" in der Reihenfolge hinzugefügt, in der sie aufgerufen werden.

Beide mySql-Befehle DROP und CREATE wenden einen TABLE_LOCK auf die betroffenen Tabellen an. Dies bedeutet, dass Ihre CREATE nicht ausgeführt wird, bis die DELETE die TABLE_LOCK freigibt.

Was wahrscheinlich geschieht, ist, dass die Reihenfolge der Befehle auszuführen, die wahrscheinlich wie folgt aussieht: (weil es länger dauert, den CREATE Befehl zur Vorbereitung und Warteschlange, als die ersten paar INSERTS)

  1. TABLE DELETE (Sperrtabellen)
  2. INSERT ...
  3. INSERT ...
  4. INSERT ...
  5. CREATE TABLE (lock table)
  6. INSERT ...

PHP wartet nicht auf einen MySQL-Befehl ...

beenden ... es weiterhin nur auf sie zu senden Befehle fröhliche Art und Weise ist. DIESE ist die Quelle Ihres Problems hier.

innoDB Transaktionen

Kann nicht Transaktionen verwenden, das Problem zu lösen. CREATE and DROP will commit a transaction.

Werfen Sie einen genaueren Blick auf Ihr $db Objekt.

Verwenden Sie eine, die basierend auf der erfolgreichen Ausführung der Abfrage ein Ergebnis zurückgibt, z. B. PDO::exec. Dann sollten Sie in der Lage sein, Ihre INSERT Anweisungen in einen bedingten Block (wo PHP auf eine Antwort warten müsste, bevor es die Bedingung überprüfen konnte) zu wickeln.

$result = $db->exec('CREATE ...'); 

if (false !== $result && $result >= 0) { 
    foreach($dataArray as $data){ 
     // INSERT 
    } 
} 
+0

Beachten Sie, dass 'table_temp' bereits am Ende dieser Skriptausführung gelöscht wird. Nur in sehr seltenen Fällen sollte der "DROP TABLE IF EXISTS table_temp;" am Anfang immer etwas finden, das man fallen lassen kann, so dass nichts gesperrt werden kann. Ich werde immer noch versuchen, diese 2 Anweisungen in ihre eigenen Transaktionen zu wickeln. Ich melde mich morgen wieder, wenn das Problem erneut auftritt! – user2015253

+0

@ user2015253, ja, ich habe es bemerkt. Die Reihenfolge der Ereignisse gilt jedoch weiterhin, da dies die beiden Ursachen nicht ändert: 1. php wartet nicht auf die Beendigung des SQL-Befehls, bevor es weitere Befehle sendet. 2. CREATE TABLE dauert länger als INSERT vorzubereiten. Ergebnis ist, dass mehrere INSERTS vor dem Start von CREATE vorbereitet und ausgeführt werden können. –

+0

Ich bin mir ziemlich sicher, dass 'CREATE' (oder irgendeine andere DDL) eine Transaktion beendet. –

0

keine Chance Schluckauf:

CREATE TABLE new LIKE real; 
...load the data into `new`... 
RENAME TABLE real TO old, new TO real; -- Atomic and 'instantaneous' 
DROP TABLE old; 

Keine Transaktionen benötigt werden, usw.

Ein mögliches Problem ist, dass die 'Last' Daten von einem sich bewegenden Ziel immer sein könnte.

Eine weitere Optimierung

Verwenden Sie eine 'Partie' INSERT - das ist eine einzige INSERT Anweisung mit mehreren Reihen. Es ist 10 mal so schnell wie die entsprechende Anzahl von 1-reihigen INSERTs. Ich empfehle 100-1000 Zeilen pro Batch.

+0

Ich habe noch nie 'RENAME TABLE real TO alt, neu TO real;' vorher gesehen. Vertauscht das sofort die Namen dieser Tabellen? Das wäre manchmal erstaunlich nützlich!Ich werde versuchen, wenn das hilft und später Bericht erstatten :) – user2015253

+0

@ user2015253 - Ich habe eine Reihe von Tipps; Dieser kommt in http://mysql.rjweb.org/doc.php/deletebig und ein paar Seiten dazu. –

+0

Danke, das ist sehr hilfreich! Leider ist das Problem gerade wieder aufgetreten, obwohl der RENAME die Table Downtime reduziert, behebt es dieses Problem nicht :( – user2015253

Verwandte Themen