2017-09-06 11 views
2

私はMySQLテーブルで作業していますが、各行に1つの値を増やす必要があります。PHP MySQL - 6.5m行のパフォーマンスの問題を修正する

col型はvarcharであり、整数または文字列(つまり、+1)を含むことができます。テーブルタイプはMyISAMです。

私はPHPでこれを試みた:

$adjust_by = 1; 
    foreach ($options as $option) { 
     $original_turnaround = $option['turnaround']; 
     $adjusted_turnaround = $option['turnaround']; 

     if (preg_match('/\+/i', $original_turnaround)) { 
     $tmp = intval($original_turnaround); 
     $tmp += $adjust_by; 
     $adjusted_turnaround = '+'.$tmp; 
     } else { 
     $adjusted_turnaround += $adjust_by; 
     } 

     if (!array_key_exists($option['optionid'], $adjusted)) { 
     $adjusted[$option['optionid']] = array(); 
     } 

     $adjusted[$option['optionid']][] = array(
     'original_turn' => $original_turnaround, 
     'adjusted_turn' => $adjusted_turnaround 
    ); 
    }//end fe options 

    //update turnarounds: 
    if (!empty($adjusted)) { 
    foreach ($adjusted as $opt_id => $turnarounds) { 
     foreach ($turnarounds as $turn) { 
     $update = "UPDATE options SET turnaround = '".$turn['adjusted_turn']."' WHERE optionid = '".$opt_id."' and turnaround = '".$turn['original_turn']."'"; 
     run_query($update); 
     } 
    } 
    } 

このアプローチの重大なパフォーマンス上の問題がある明白な理由。私のローカル開発環境でこれを実行すると、多くのエラーが発生し、最終的にサーバーがクラッシュします。

私が考慮する必要がある別のことは、これが運用環境で実行されるときです。これはeコマースストア向けのもので、このようにデータベースをロックしたり、その他の問題を引き起こしたりするような膨大なアップデートはできません。私が発見した

一つの可能​​な解決策はこれです:Fastest way to update 120 Million records

しかし、別のテーブルを作成することは、それ自身の問題が付属しています。コードベースが良好な状態ではないので、類似のクエリがこのテーブルで複数の場所で実行されるため、このアプローチを有効にするために多数のクエリとファイルを変更する必要があります。

私のオプション(あれば)には何がありますか?

+0

"これは実稼働環境で実行されるときに考慮する必要があります。これはeコマースストア用であり、データベースをロックするなどの巨大なアップデートやその他の問題を引き起こすことはできません。 MyISAMエンジンは、更新または挿入時にテーブルをロックします。テーブルをInnoDBエンジンに変換することを検討する必要があります。このエンジンはテーブルを更新または挿入でロックしません。 –

+0

テーブルのインデックスが正しく設定されていますか?シンプルなインデックス作成では、これを十分にスピードアップすることができます。 – sagi

+0

PHPで集中的なタスクを実行しないでください。最大限のタイムアウトの問題を抱えている可能性があります。また、PHPはCPU集約型タスク用に設計されていません。あなたはむしろタスクのためにpythonを使うことができます。 – user254153

答えて

6

このタスクはSQLで実行できます。

  • CASTを使用すると、文字列を整数に変換できます。
  • IFSUBSTRには、文字列に+が含まれているかどうかを確認できます。
  • CONCATを入力すると、必要に応じて計算結果に+を追加します(2つの値を1つの文字列にマージします)。

ただ、このSQLを試してみてください。

"UPDATE `options` SET `turnaround` = CONCAT(IF(SUBSTR(`turnaround`, 1, 1) = '+', '+', ''), CAST(`turnaround` AS SIGNED) + " + $adjust_by + ") WHERE 1"; 
+0

素晴らしいです。これが正確に何をしているのか、少し説明してください。私のSQL知識は少し限られており、私はそれを完全に理解していません。ありがとうございました。 –

+0

@Neodan WHERE 1?常に真実だから除外することができます –

+0

これは、方程式からPHPやその他の言語を削除する正解です。これは、テキストの列の値を整数に変換し、インクリメントして保存します。これは基本的にひどいデータベーススキーマのための素晴らしい修正です。私から+1! – Mjh

1

は、あなただけの

UPDATE whatevertable SET whatever = whatever + 1を言うことができませんか?

試してみてください、私はそれが動作すると確信しています!

EDIT:文字列または整数がありますか?あなたのDB設計に欠陥がありますが、これはおそらくうまくいかないでしょうが、あなたのDB設計がより厳しくなっていれば正解になりました。

+0

はい、DBとコードベースが適切に設計されていれば、これは完璧なソリューションとなりました。残念ながらこれは私が継承したものです。主要な頭痛の原因となる主要な問題... –

+0

"継承"とは、続行する前にクリーンアップを行う必要があることを意味します。それはあなたが安っぽいデザインにこだわっていることを意味するものではありません。 –

0

あなたはおそらく持っていますが、必要はありませんが、この '複合' 指数(どちらかで順):

INDEX(optionid, turnaround) 

SHOW CREATE TABLEを提供してください。

もう1つ、わずかなパフォーマンスの向上は、その更新ループの前に明示的にLOCK TABLE WRITEです。その後はUNLOCKとなります。注意:これはMyISAMにのみ適用されます。

InnoDBのほうがはるかに良いでしょう。

関連する問題