2017-09-28 19 views
0

私は1日に一度自動的に更新されているmysqlテーブルがあります。このアップデートを行うスクリプトは、以下のPHPコードのようになります。基本的には、同じ構造の一時テーブルを作成し、最初にすべてのデータを挿入してから、実際のテーブルを切り捨てて一時テーブルからデータを挿入します。最後に一時テーブルが破棄されます。この方法は、実際の表の停止時間を大幅に短縮したために使用されます。ベーステーブルまたはビューが見つかりません - それは

これは、かなりの間、正常に動作していました。最近まで、Debianのアップグレード(Debian 8からDebian 9へ、これはMySQLからMariaDBへの切り替えも意味します)。ログファイルに次のエラーメッセージが表示されることがよくあります。

[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 

このエラーは最終的に一部のデータが失われる原因となります。私が正しく理解していれば、挿入が完了する前に一時テーブルが破棄されたと言われます。これは実際にはTEMPORARY TABLEではないことを考慮して、どうやってこれを行うことができます、私はちょうど実際のテーブルを使用し、それをtempと名付けます。 MariaDBはINSERTを遅延させるので、PHPスクリプトはINSERTが完了する前にDROPを実行できますか?

スクリプトは本当に唯一の1日1回実行されるようにこれは、cron.dエントリです:

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

また、スクリプトは1時間か2時間、そうからの実行中に実行が完了していることは注目に値します前日は長く終わり、妨げにならない。

これは基本的にコードされ:

$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

'$ table-> query(" CREATE TABLE 'table_temp' LIKE')を待たなければならないので、 'table_temp'はprepare関数の前に作成されていませんでした。テーブル;; ");'それは正常に実行されました.. –

+0

あなたのコードでWhats line 50? –

+0

行50がコードにコメントされています。それは挿入クエリの実行です – user2015253

答えて

0

ドロップ。作成します。 (おそらくロック)

ロックによって遅延されたコマンドは、呼び出された順に「キュー」に追加されます。

両方のmySqlコマンドDROPおよびCREATEは、影響を受けるテーブルにTABLE_LOCKを適用します。つまり、は、DELETETABLE_LOCKをリリースするまで実行されません。 (それはCREATEコマンドを準備し、キューに時間がかかりますので、最初の数INSERTSより)

  1. テーブルを削除(:

    何可能性が起こっていることは実行するコマンドの順序は、おそらくこのようになっていることですロック・テーブル)

  2. INSERT ...
  3. INSERT ...
  4. INSERT ...
  5. CREATE TABLE(ロック表)
  6. INSERT ...

PHPは...

を完了するためにmysqlコマンドを待ちません...それはちょうどそれがコマンドを送信陽気な方法だ上で続けています。 THATが問題の原因です。

innoDBトランザクション

トランザクションを使用して問題を解決することはできません。 CREATE and DROP will commit a transaction

$dbオブジェクトを詳しく見てください。

PDO::execのように、クエリの実行に基づいて結果を返すものを使用します。 INSERTステートメントを条件付きブロックにラップすることができます(PHPが条件を確認する前に応答を待つ必要がある場合)。しゃっくりの

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

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

'スクリプトの実行終了時に 'table_temp'が既に削除されていることに注意してください。 'DROP TABLE IF EXISTS table_temp;'は初めに落とすものが見つかるので、何もロックすることはできません。私はまだそれらの2つのステートメントを自分のトランザクションにラップしようとします。問題が再び発生した場合は明日に報告します! – user2015253

+0

@ user2015253、はい、私は気づいた。しかし、次の2つの原因は変わらないので、イベントの順序はそのまま適用されます。1. phpはSQLコマンドの完了を待ってからコマンドを送信します。 2. CREATE TABLEはINSERTよりも準備に時間がかかります。 CREATEの開始前にいくつかのINSERTを準備して実行することができます。 –

+0

私はかなり 'CREATE'(または他のDDL)がトランザクションを終了すると確信しています。 –

0

ゼロのチャンス:必要

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

ない取引など

の1つの潜在的な問題は、「負荷」は、いくつかの移動ターゲットからデータを取得することができることです。

別の最適化

'バッチ' INSERT使用 - 複数の行を持つ単一のINSERT文です。これは、1行の等価数の10倍の速さになりますINSERTs。バッチあたり100〜1000行をお勧めします。

+0

私はこれまで、 'RENAME TABLE real TO old、new TO real;'を見たことがありません。これは即座にそれらのテーブルの名前を交換しますか?それは時々驚くほど役に立つでしょう!私はそれが後で助けて、報告してくれたらやってみよう:) – user2015253

+0

@ user2015253 - 私はたくさんのヒントを持っている。それはhttp://mysql.rjweb.org/doc.php/deletebigに表示され、そこからリンクされたいくつかのページが表示されます。 –

+0

ありがとう、それは非常に便利です!残念ながら、問題は再び発生したので、RENAMEはテーブルのダウンタイムを減らしますが、この問題は修正されません:( – user2015253

関連する問題