2011-12-20 3 views
1

Linux上で30個のスクリプト(PHP CLI)を実行しています。各スクリプトはMySQLデータベース内のデータを更新(ループ)しています。`update`パフォーマンスを改善しました(行ロック問題)

端末に「mysqladmin proc」と入力すると、多くの行が10〜30秒間ロックされていることがわかります。ほとんどの場合、Updateのキューです。どのようにパフォーマンスをより速く改善する?私はInnoDBエンジンを使用しています。

PHPスクリプトは次のようになり:

//status and process are indexed. 
$SQL = "SELECT * FROM data WHERE status = 0 AND process = '1'"; 
$query = $db->prepare($SQL); 
$query->execute(); 

//about 100,000+ rows for each script 
while ($row = $query->fetch(PDO::FETCH_ASSOC)) { 
     checking($row); 
     sleep(2); 
} 

function checking($data) { 

    $error = errorCheck($data['number']); 

    if ($error) { 
    //number indexed 
    $SQLUpdate = "UPDATE data SET status = 2, error='$error' WHERE number = " . $data['number']; 
    $update = $db->prepare($SQLUpdate); 
    $update->execute(); 
    return false 
    } 


    //good? 
    $SQLUpdate = "UPDATE data SET status = 1 WHERE number = " . $data['number']; 
    $update = $db->prepare($SQLUpdate); 
    $update->execute(); 


    $SQLInsert = "INSERT INTO tbl_done ....."; 
    $SQLInsert = $db->prepare($SQLInsert); 
    $SQLInsert->execute(); 
} 

topコマンド:

top - 10:48:54 up 17 days, 10:30, 2 users, load average: 1.06, 1.05, 1.01 
Tasks: 188 total, 1 running, 187 sleeping, 0 stopped, 0 zombie 
Cpu(s): 25.8%us, 0.1%sy, 0.0%ni, 74.1%id, 0.0%wa, 0.0%hi, 0.1%si, 0.0%st 
Mem: 4138464k total, 1908724k used, 2229740k free, 316224k buffers 
Swap: 2096440k total,  16k used, 2096424k free, 592384k cached 

    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
32183 mysql  15 0 903m 459m 4800 S 101.8 11.4 876:53.66 mysqld 

-

/etc/my.cnf 
[mysqld] 
set-variable = max_connections=500 
safe-show-database 
max_user_connections=200 
key_buffer_size = 16M 
query_cache_size = 350M 
tmp_table_size = 200M 
max_heap_table_size = 200M 
thread_cache_size = 4 
table_cache = 800 
thread_concurrency = 8 
innodb_buffer_pool_size = 400M 
innodb_log_file_size = 128M 
query_cache_limit = 500M 
innodb_flush_log_at_trx_commit = 2 

サーバー仕様:インテルCore 2クワッドQ8300、2.5 GHzの、4GBのRAMを。

'のmysqladmin procを':

+------+-----------------+-----------+----------------+---------+------+----------+------------------------------------------------------------------------------- 
| Id | User   | Host  | db    | Command | Time | State | Info                   
+------+-----------------+-----------+----------------+---------+------+----------+-------------------------------------------------------------------------------- 
| 265 | user   | localhost | xxxxxxxxxxxxxx | Query | 15 | Updating | UPDATE data SET status = '2', error = 'Unknown error' WHERE number= 0xxxxx  
| 269 | user   | localhost | xxxxxxxxxxxxxx | Query | 17 | Updating | UPDATE data SET status = '2', error = 'Invalid ....' WHERE number= 0xxx 
| 280 | user   | localhost | xxxxxxxxxxxxxx | Query | 7 | Updating | UPDATE data SET status = 1 WHERE f = 0xxxx           
| 300 | user   | localhost | xxxxxxxxxxxxxx | Query | 1 | Updating | UPDATE data SET status = '2', error = 'Unknown ....' WHERE number= 0xx    
| 314 | user   | localhost | xxxxxxxxxxxxxx | Query | 13 | Updating | UPDATE data SET status = '2', error = 'Invalid....' WHERE number= 0xxxx 
| 327 | user   | localhost | xxxxxxxxxxxxxx | Query | 11 | Updating | UPDATE data SET status = '2', error = 'Unknown ....' WHERE number= 0xxxx    
| 341 | user   | localhost | xxxxxxxxxxxxxx | Sleep | 2 |   | NULL                      
| 350 | user   | localhost | xxxxxxxxxxxxxx | Query | 7 | Updating | UPDATE data SET status = '2', error = 'Unknown ....' WHERE number= 0xxx     
| 360 | user   | localhost | xxxxxxxxxxxxxx | Query | 5 | Updating | UPDATE data SET status = 1 WHERE number = 0xxxx  

は説明:

+----+-------------+-------+-------------+----------------+----------------+---------+------+-------+----------------------------------------------+ 
| id | select_type | table | type  | possible_keys | key   | key_len | ref | rows | Extra          | 
+----+-------------+-------+-------------+----------------+----------------+---------+------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | data | index_merge | process,status | process,status | 52,1 | NULL | 16439 | Using intersect(process,status); Using where | 
+----+-------------+-------+-------------+----------------+----------------+---------+------+-------+----------------------------------------------+ 
+0

'checking($ row);'行は 'checkNumber($ row);'としましたか? – melihcelik

+0

はい申し訳ありませんが、間違い、私の質問を修正しました。 –

+0

'create table'の結果を提供できますか? – melihcelik

答えて

2

、あなたが読まれた行のロックを読んで取得されています。また、checkメソッド内で現在読み込み中の(= locked)行を更新しようとしています。したがって、MySQLはselectクエリによって読み取りロックが解除されるとすぐに実行される更新クエリをキューに入れます。しかし、すべての行で2秒間実行を中断したので、ロックが解放されるまでの遅延が増えているため、更新キューで待機しているすべてのクエリが遅れます。 innodb lock modesについて詳しく読むことができます。

私は、次のようなコードを変更することをお勧め:

  • は、行の限られた量を返し、それが次の反復の間に、残りの行を選択しますことを確認するために、選択クエリを制限します。選択クエリにoffsetlimit文を使用すると、これを実現できます。
  • は更新、
  • が最初に進み、すべての行を変数の配列に選択クエリからすべての行を読み取り、その読み取りロックが数字のあなたのアレイの上だけでなく
  • 反復をリリースされますので、クエリをリリースあなたが中断したところから一歩。

UPDATE

あなたは、結果セットから行を取得するためにfetchを使用しています。documentationによると:

は、一度にすべての行を取得するためにはPDOStatementオブジェクト

に関連付けられた結果セットから行を取得するには、fetchAllを使用しますが、パフォーマンス上の問題の注意する必要がありますdocumentationの状態:

この方法を使用して大きな結果セットをフェッチすると、システムおよびネットワークリソースの需要が大きくなります。

これは、結果セット全体(100.000+行からなる)ではなく、特定の量の行を取得するようにクエリを制限することを提案した理由です。クエリ秒の時間を実行したときのようなクエリを実行し、その後、

SELECT * FROM data WHERE status = 0 AND process = '1' LIMIT 10000 OFFSET 0 

そして:あなたがするまで、このように継続することができ

SELECT * FROM data WHERE status = 0 AND process = '1' LIMIT 10000 OFFSET 10000 

あなたは次のようにクエリを変更することによって返される行の数を制限することができます結果は返されません。

+0

おかげで詳細な答えは、私は試してみましょう!あなたは限界を言っていますが、どれくらいの限度ですか?その例と、次の最初のステップに進む方法を教えてください。 ...私はちょうど私の質問を更新し、 'mysqladmin proc'の結果を含む..これは私が問題を抱えているところです。 –

+0

'$ query-> fetch'はすでにすべてのデータを' $ row'変数にフェッチしました。次の反復のためにデータベースから再度チェックしません。あなたは言った: "選択されたクエリからすべての行を変数の配列に読み込む" - これが行われたことです。 –

+0

すべての行を検索するのに 'fetchAll'を使用していないので、' fetch'は結果セットの1行だけを取り出します。ここでドキュメントを見つけることができます:http://www.php.net/manual/en/pdostatement.fetch.php – melihcelik

0
  1. をあなたは、ステータス= 1のすべての行は、ステータス= 2にエラーが発生した行を更新 - これは仮定しますタイプミス(他の行方不明)

  2. 実際に各行の間に2秒間眠っている場合は、「li MIT 1" と2秒ごとに選択クエリを再実行する - これはあなたのロックを占める可能性があり、番号が一意でない場合は、selectクエリを実行すると

+0

1)質問を修正しました。エラーのために 'return false'を追加しました。 2)私はそれは良いアイデアだとは思っていない、私は '選択 'と問題はありません。それはちょうど '更新'問題がプロセスにロックされている –

関連する問題