cakephp2.x遅く、重いsaveallの50倍速いbulkinsertを使おう

2015年12月19日更新 view: 259 view
http://ecx.images-amazon.com/images/I/51li6EcTU+L.jpg

cakephpのsaveallはくそ重くて、スクレイピングに使えない

スクレイピングしようとやっていたらもうデータを入れるだけでmysqlが悲鳴を上げ、涙を流し始める。
なんでだろう?
原因を探った。

スポンサードリンク

バルクインサートって何よ?

INSERT INTO kabu_tmps (id, body, info) VALUES ('1','みかん','やわらかい'),('2','りんご','あかい') ON DUPLICATE KEY UPDATE body = VALUES(body) , info = VALUES(info)

こんな感じで、ロングなsql文を一発で処理できるのをバルクインサートという。
じゃあ、cakephpのsaveallは?というと・・・。

わかってますよね。
cakephpはsaveをforeachで回そうが、
saveallを使おうがめちゃくちゃなクエリを発行するので激重になる。

どんくらいちゃうん?

saveメソッド+forループ saveAllメソッド queryメソッド
1回目 14.3828558922 15.3304040432 0.311769008636
2回目 14.4124500751 15.4717850685 0.304137945175
3回目 14.5008730888 15.3734378815 0.319990158081

その差 50倍。

上記のデータは
http://damepg.hatenablog.com/entry/2012/10/01/001223
から引用させていただきました。

では、cakephpでバルクインサートを使います。

AppModel.phpに追記

/*
* $data = [
0 => [
'id' => 1,
'body' => "みかん",
'info' => "やわらかい"
],
1 => [
'id' => 2,
'body' => "りんご",
'info' => "あかい"
],
];
*
*
*
*/

public function bulkInsert($data) {

if(count($data) > 0) {

App::uses('Sanitize', 'Utility');
$data = Sanitize::clean($data);

$value_array = array();

$tmp = $data;
$first_data = array_shift($tmp);

if(isset($first_data[$this->name])){

$fields = array_keys($first_data[$this->name]);
foreach ($data as $key => $v) {
$value_array[] = "('" . implode('\',\'', $v[$this->name]) . "')";
}

} else {
$fields = array_keys($first_data);
foreach ($data as $key => $v) {
$value_array[] = "('" . implode('\',\'', $v) . "')";
}
}

$table = $this->tablePrefix.$this->useTable;

$dup_update = [];
foreach ($fields as $v) {

$dup_update[] = $v." = VALUES(".$v.")";
}

$dup_update = implode(",",$dup_update);




$sql = "INSERT INTO " . $table . " (" . implode(', ', $fields) . ") VALUES " . implode(',', $value_array)." ON DUPLICATE KEY UPDATE ".$dup_update;


$this->query($sql);
return true;
}

return false;
}

controller.php

        $data = [
0 => [
'id' => 1,
'body' => "みかん",
'info' => "やわらかい"
],
1 => [
'id' => 2,
'body' => "りんご",
'info' => "あかい"
],
];

$this->Model->bulkInsert($data);

これで音速のsaveが完了します。
ちょこまかしたデータならまだしも、
スクレイピングで大量データを保存するときの
saveallを使うのは絶対にやめましょう。

スポンサードリンク

関連記事

関連カテゴリ