質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

Q&A

解決済

3回答

6793閲覧

CakePHPでマルチプロセスがうまく動きません。

noripi

総合スコア34

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

1グッド

1クリップ

投稿2016/11/01 09:55

編集2016/11/02 04:11

CakePHPのコンポーネントで時間のかかる処理をマルチプロセスにしたいのですが、一番最初のfor文のループが2回目になると下記エラーとなってしまいます。
マルチプロセスの処理を入れないと正常に動作します。
また、my.cnfのクエリの最大サイズやタイムアウトなどを変更してみましたがダメでした。
あまりサーバーサイドは得意ではないのでアドバイスお願いします。

《エラー》

error

1Warning Error: Error while sending QUERY packet. PID=27017 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] 2 3Error: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away 4#0 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(468): PDOStatement->execute(Array) 5#1 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(434): DboSource->_execute('SELECT `Crawlin...', Array) 6#2 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(671): DboSource->execute('SELECT `Crawlin...', Array, Array) 7#3 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(1120): DboSource->fetchAll('SELECT `Crawlin...', false) 8#4 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3038): DboSource->read(Object(CrawlingContent), Array) 9#5 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3010): Model->_readDataSource('all', Array) 10#6 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(1414): Model->find('all', Array) 11#7 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(36): ScrapingComponent->crawler_index_multi() 12#8 /home/scraper/public_html/ec_scraper/Console/Command/ScraperShell.php(65): ScrapingComponent->test() 13#9 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(458): ScraperShell->test() 14#10 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(212): Shell->runCommand('test', Array) 15#11 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch() 16#12 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/cake.php(54): ShellDispatcher::run(Array) 17#13 {main} 18

エラー部分のコード

php

1 //データを分割取得 2 for ($page=1; $page <= $totalPage; $page++) { 3 4 $result = $CrawlingContent->find('all', array( 5 'conditions' => $conditions, 6 'recursive' => -1, 7 'limit' => $limit, 8 'page' => $page, 9 )); 10 11 if ($limit > ($count - $totalAccess)) { 12 $limit = $count - $totalAccess; 13 } 14 15 //初期化 16 $mkp = $pcount; 17 //取得データ 18 for ($access=0; $access < $limit;) { 19 20 if ($mkp > ($limit - $access)) { 21 $mkp = $limit - $access; 22 } 23 24 for ($i=0; $i < $mkp; $i++) { 25 $pid = pcntl_fork(); 26 if( $pid === -1 ) { 27 debug('マルチプロセスのforkに失敗'); 28 exit; 29 }elseif($pid === 0){ 30 //子プロセス 31 debug($result[$i]['CrawlingContent']['url']); 32 exit; 33 } 34 // 親プロセスの処理 35 $processes[] = $pid; 36 } 37 38 foreach($processes as $process) { 39 pcntl_waitpid($process, $status); 40 } 41 42 $access = $access + $i; 43 $totalAccess = $totalAccess + $i; 44 } 45}

子プロセスでのDB接続を試してみましたがダメでした。

php

1 for ($i=0; $i < $pcount; $i++) { 2 $pid = pcntl_fork(); 3 if( $pid === -1 ) { 4 debug('マルチプロセスのforkに失敗'); 5 exit; 6 }elseif($pid === 0){ 7 //子プロセス 8 if ($i===($pcount-1)) { 9 $endPage = ($endPage - $processPage) + $lastProcessPage; 10 } 11 12 $page = $startPage; 13 14 for ($page; $page <= $endPage; $page++) { 15 16 $result = $CrawlingContent->find('all', array( 17 'conditions' => $conditions, 18 'recursive' => -1, 19 'limit' => $limit, 20 'page' => $page, 21 )); 22 foreach ($result as $resultValue) { 23 debug($resultValue['CrawlingContent']['url']); 24 } 25 26 } 27 exit; 28 } 29 // 親プロセスの処理 30 $processes[] = $pid; 31 $startPage = $startPage + $processPage; 32 $endPage = $endPage + $processPage; 33 } 34 35 foreach($processes as $process) { 36 pcntl_waitpid($process, $status); 37 }

エラーコード

Warning Error: Packets out of order. Expected 1 received 3. Packet size=262144 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: Packets out of order. Expected 4 received 97. Packet size=7496534 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: Packets out of order. Expected 1 received 99. Packet size=6621798 in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: PDOStatement::execute(): MySQL server has gone away in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: PDOStatement::execute(): Error reading result set's header in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Warning Error: PDOStatement::execute(): Error reading result set's header in [/home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php, line 468] Error: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away #0 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(468): PDOStatement->execute(Array) #1 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(434): DboSource->_execute('SELECT `Crawlin...', Array) #2 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(671): DboSource->execute('SELECT `Crawlin...', Array, Array) #3 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Datasource/DboSource.php(1120): DboSource->fetchAll('SELECT `Crawlin...', false) #4 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3038): DboSource->read(Object(CrawlingContent), Array) #5 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Model/Model.php(3010): Model->_readDataSource('all', Array) #6 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(1434): Model->find('all', Array) #7 /home/scraper/public_html/ec_scraper/Controller/Component/ScrapingComponent.php(36): ScrapingComponent->crawler_index_multi() #8 /home/scraper/public_html/ec_scraper/Console/Command/ScraperShell.php(65): ScrapingComponent->test() #9 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/Shell.php(458): ScraperShell->test() #10 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(212): Shell->runCommand('test', Array) #11 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/ShellDispatcher.php(66): ShellDispatcher->dispatch() #12 /home/scraper/public_html/ec_scraper/Vendor/cakephp/cakephp/lib/Cake/Console/cake.php(54): ShellDispatcher::run(Array) #13 {main}
ikuwow👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

子プロセスが終了した時点で、MySQLのコネクションが破棄されてしまうようですね。
以下、参考になりそうな文献

※Cakephpの内部ではPDOが使われており、一つのコネクションでSQLを発行しているはずです。たぶんCakephpないでpcntl_forkを使う事は想定されていないので、仕方ない気がします...。もしかしたら、子プロセス内で初めてSQLを実行すれば、子プロセス内でコネクションが作られるので、問題なく動くかもしれません(要件を満たすかはわかりませんが)

投稿2016/11/01 14:10

編集2016/11/01 14:18
popobot

総合スコア6586

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

noripi

2016/11/02 00:45

ご回答ありがとうございます。 icchiiさんとA.lchiさんのご回答を参考に子プロセスでそれぞれDB接続するようにして試してみます。
popobot

2016/11/02 01:06 編集

時間のかかる処理というのが具体的に何かわからないので的確なコメントはできませんが... なるべくマルチプロセス化以外の方法を模索したほうがいいかもしれませんよ。 ※PHPで1リクエストの処理をマルチプロセスで処理するなんて聞かないですし...。 自分だったら、バッチで事前に時間のかかる処理を実行した結果をDB等に持っておくとかよくやります。
noripi

2016/11/02 04:22

子プロセスにて試してみましたがダメでした。。。 内容はDB内にある大量のテキストデータの一部を抜き出した結果を別テーブルに格納するものです。 時間的制約と他言語の知識不足、もともとCakePHPで組まれていたため、このままCakePHPでなんとかならないかなって感じでした。。。
popobot

2016/11/02 04:28

事前に抜き出しておくとかではイケないのですか? 毎回抜き出すキーワードが違うとか?
noripi

2016/11/02 05:10

一度に抜き出そうとするとメモリ不足となってしまうので、メモリ不足にならないギリギリのところで、リミットをかけて分割取得しています。。。 もちろん。php.iniでメモリサイズは変更済みです。
noripi

2016/11/02 05:13

はい。処理によって抜き出すキーワードはかわります。
popobot

2016/11/02 05:19

メモリ不足が問題なら、マルチプロセスである必要はなくて、単にfindするときにpageとlimitで件数を絞って処理すればいいだけな気がしますよ!!
noripi

2016/11/02 06:00

はい。メモリ不足に関してはpageとlimitでの対処を検討してました。 一度、シングルで実行時間のテスト結果から総合的な実行時間を計算した結果、実用的な状態ではなかったので、今回マルチプロセスの選択となりました。。。
popobot

2016/11/02 06:10

なるほど! 時間がかかっているのがCPUボトルネックだったら、マルチプロセスにすればCPUの数だけ早くはなりますが、1リクエストでCPUを全部専有するような処理では実用性は怪しい気もしますが...まぁ利用者が限定的だったらそれでもいいのかもしれませんが。 根本的には全文検索システムとか導入するとかしないとなんとも言えない気がしました。(使っているならチューニングとか)
noripi

2016/11/02 06:50

はい。利用者は基本1名で操作時間も限定的で集計したデータを翌日見る感じです。 なるほど。全文検索システム未導入でした。既存(CakePHP)のシステムにも導入は可能なのでしょうか。 また、今回の質問の件で色々調べたのですが、PDOが怪しそうです。なにか設定とかご存知ではないでしょうか。
popobot

2016/11/02 07:15 編集

> なるほど。全文検索システム未導入でした。既存(CakePHP)のシステムにも導入は可能なのでしょうか。 MySQLは標準で全文検索インデックスをサポートしています。SQLが変わりますが、Cakephpからでも実行は可能です。 https://dev.mysql.com/doc/refman/5.6/ja/fulltext-search.html また、日本語向けに改善されたmroongaというライブラリもあります。 http://mroonga.org/ja/ > また、今回の質問の件で色々調べたのですが、PDOが怪しそうです。なにか設定とかご存知ではないでしょうか。 そうですね。Cakephpでは内部的にはPDOを使っており、そのPDOのコネクションが子プロセスにも渡されており、子プロセス終了時にコネクションが切れているんだと思います。 ちょっと調べてみましたが、以下のStackover flowによれば、reconnectという関数があるようなので、findする前に毎回reconnectすればうまくいくかもしれません。 http://stackoverflow.com/questions/10051295/mysql-server-has-gone-away-in-cakephp-bake-console ※実際Cake2のコードをみるとたしかにそういう関数は存在していました。使ったことはないですが、試してみてください。
noripi

2016/11/02 08:08

お付き合い頂きありがとうございます。 またまた、大変勉強になります。 「reconnect」ですが、なんだかいけそうな匂いがしますね! 早速、試させていただきます。
noripi

2016/11/02 13:06

find前にreconnectすることで見事に解決しました。本当にありがとうございました。
popobot

2016/11/02 22:46

ちょっと予想外の展開でしたが、うまくいってよかったです! 結果、処理は実用的なレベルになったか心配ですが....
noripi

2016/11/04 01:00

ご心配頂きありがとうございます。 その後、実行時間のテストをしましたが、シングル時の半分以下の時間で完了しました。 今回の処理では、効果有りのようです。
noripi

2016/11/04 01:15

ちなみに、ですがreconnectの前に一度接続の確認を入れたほうがいいのでしょうか。 また、必要な場合、CakePHPに接続を確認するような関数はご存知ですか。検索してみましたがそれらいしものが見当たりませんでした。 サンプルではmysql_pingを使っていましたが現在非推奨のようで、PDOを使えと書いてあります。。。
popobot

2016/11/04 01:29 編集

おお、改善されて良かったです!! マルチプロセス化すれば確実にコネクション切れるので、確認しなくてもいいと思いますよ。 またreconnect()関数は、DB設定を動的に変更して、再接続したいときに使うもののようなので、内部的にはdisconnectしてからconnectしているので、接続が切れていないくても正常に動きます。 ※Cakeのコードを見て確認しただけなので、動作確認は別途やってください。 なお、getConnection()関数で、PDOの生コネクションを取得できるので、そこから確認することもできると思いますけど
noripi

2016/11/04 01:53

なるほど。そうなのですね。 大変助かりました。本当にありがとうございます。感謝です。
guest

0

データベースでエラーが発生している様ですので、子プロセスには、データベースへのアクセスが行われた中で発生している様に見れます。

一般的にforkされる場合、自身のコピーを作成してサブミットし、同ステップより開始されるイメージです。
その際、親から切り離され際にメモリはコピーはされますが、共有はできない状態となります。

親プロセスでDB接続をオープンしたままの状態で子プロセスを生成してしまうと、子プロセスで接続ファイルIDが同じものを使ってしまい、1つの子プロセスが終了すると、他の子プロセスのDB接続がクローズされてしまいます。
ですのでフォークする場合、各子プロセスで新たにDB接続してアクセスを行います。

CakePHPフレームワークの知識が無いので、DBへの接続が個々に行えるのかが分かりません。(調べてみます)

phpのつたない例ですが

php

1 for ($i = 1; $i <= 5; ++$i) { 2 $pid = pcntl_fork(); 3 if (!$pid) { 4 sleep(1); 5 print "In child $i\n"; 6 7 try{ 8 $dbh = new PDO($dsn, $user, $password); 9 }catch (PDOException $e){ 10 print('Connection failed:'.$e->getMessage()); 11 die(); 12 } 13 $sql = 'select * from tableA'; 14 $stmt = $dbh->query($sql); 15 16 while($result = $stmt->fetch()){ 17 print($result['UserID']." "); 18 print($result['station_id1']."\n"); 19 } 20 21 exit($i); 22 } 23 }

投稿2016/11/01 23:08

編集2016/11/02 04:39
A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

noripi

2016/11/02 00:41

ご回答ありがとうございます。 なるほど、勉強になりますl。となると、icchiiさんもおっしゃられてたように、子プロセスでそれぞれDB接続してデータを取得するようにして試してみます。
noripi

2016/11/02 04:15

子プロセスにて試してみましたがダメでした。。。
A.Ichi

2016/11/02 04:37

CakePHPがよく読めていませんで申し訳ありませんが、//子プロセス以下の場所で、DB接続が新たにされているとの認識でよろしいでしょうか?
noripi

2016/11/02 05:04

はい。その通りです。
guest

0

ちょっとアレな回答ですが。
私は、curl_multiで自ホストにAPIコールという形でマルチリクエストを投げる形で解決したことがあります。
この場合、HTTPリクエスト/レスポンスによるオーバーヘッドは必要になりますが、問題なく並列処理は可能です。

投稿2016/11/02 04:51

kunai

総合スコア5405

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

noripi

2016/11/02 06:11

ご回答ありがとうございます。 curl_multiですが、私の認識ではHTTPのリクエストを並列化するものだと思っていました。 今回は時間のかかるテキスト解析をマルチプロセスにより、短縮したいというものでした。なにか、使いかたがあるのでしょうか。 もし、参考サイトなどありましたらお知らせ頂ければ幸いです。
kunai

2016/11/02 07:10

「HTTPのリクエストを並列化するもの」 その通りですよ。 自分のサイトに対して、リクエストを複数投げる感じです。 そのテキスト解析を、例えば1MB毎にデータを分割して、それぞれのリクエストで分割したデータ一つずつ解析し、結果をリクエスト元でまとめるというような。
noripi

2016/11/02 08:02

なるほど。そのような発想はありませんでした。大変勉強になります。 この方法なら、確かにいけそうですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問