PHP×mongoDBでいろいろな検証
環境
・VMWareでCentOS5.8
・PHP 5.4
・MongoDB 2.6
countの速度検証
// $collection は new MongoCollection 云々で定義したやつ // $condition は取得条件の配列 // ① $count_a = $collection->count($condition); // ② $count_b = $collection->find($condition)->count();
①②それぞれ1万回連続実行を5回試行しました。(単位は秒)
① | 2.81 | 3.28 | 2.99 | 2.75 | 2.45 |
② | 2.86 | 3.21 | 3.07 | 2.81 | 2.59 |
①の方がほんのり速いですね。
カーソルの挙動
まあそもそもカーソルを理解できてない感あるのですが
// $collection は new MongoCollection 云々で定義したやつ
// $condition は取得条件の配列
$cursor = $collection->find($condition);
// ①取得結果を時間かけてぐるぐる
foreach ($cursor as $document)
{
// 3秒待つ
usleep(3000000);
// 表示
var_dump($document['str']);
}
①が時間かけて動いてる間にinsert連打したらどうなるか。
結果、まあinsert内容はここでは反映されませんでした。
しかも、insertはここの処理が終わった後に実行…ってロックかかってんのかよ!!
このやり方で時間のかかる処理をするのはよろしくなさそうですね。
調べてみた感じ、DBレベルのロックである模様。コレクションレベルですらないのか…
ただし、バージョン2.8からドキュメントレベルのロックになった模様?
http://blog.odoruinu.net/2014/11/13/mongodb-2-8-0-rc0/
collectionとってくるときの速度検証
$mongo = new MongoClient("mongodb://localhost:27017"); // ① $db = $mongo->selectDB("DB名"); $collection = new MongoCollection($db, "コレクション名"); // ② $collection = $mongo->selectDB("DB名")->selectCollection("コレクション名");
①②それぞれ100回連続実行を5回試行しました。(単位はミリ秒)
① | 0.508 | 0.532 | 0.549 | 0.542 | 0.557 |
② | 0.544 | 0.525 | 0.518 | 0.573 | 0.595 |
変わんね
まあこれなら取り回しやすい①で良いかな。
いろんなDB使う状況よりも、1つのDBのいろんなコレクション使う状況の方が多いだろうし。
(当然「$db = ...」の行を処理時間計測部分から外すと①の方が速いです)
insert VS batchInsert(ついでにMySQLも)
とにかくInsertする速度を単純に計る。
- データを100件用意($insert_list)
- mongoにはフィールド「a」を入れる
- mysqlには「test」テーブル(`id`(PK,autoincrement), `a`)を作って「a」に値を入れる
// $insert_list:array(array('a' => "hoge_1"), array('a' => "hoge_2"), ...) // $collection:mongoのtestコレクション // ①[MongoDB]insertを100回 for ($i = 0; $i < 100; $i ++) { $collection->insert($insert_list[$i]); } // ②[MongoDB]batchInsertを1件×100回 for ($i = 0; $i < 100; $i ++) { $collection->batchInsert(array($insert_list[$i])); } // ③[MongoDB]batchInsertを100件×1回 $collection->batchInsert($insert_list); // ④[MySQL]INSERTを1件×100回 for ($i = 0; $i < 100; $i ++) { クエリ実行("INSERT INTO `test`(`a`) VALUES (" . $insert_list[$i]['a'] . ") "); } // ⑤[MySQL]INSERTを100件×1回 // (面倒なのでクエリ組み立てのごちゃごちゃは省略 察しろ) クエリ実行("INSERT INTO `test`(`a`) VALUES (" . $insert_list[0]['a'] . "), (" . $insert_list[1]['a'] . "), ... ");
まあこんな感じのを5回ずつ試行した結果がこちら。単位はミリ秒。
① | 24.480 | 23.633 | 35.464 | 36.615 | 28.074 |
② | 20.702 | 22.661 | 26.355 | 21.509 | 22.932 |
③ | 2.667 | 1.759 | 1.570 | 1.641 | 1.935 |
④ | 241.351 | 170.492 | 184.373 | 185.667 | 205.206 |
⑤ | 19.397 | 26.420 | 20.548 | 23.061 | 23.316 |
いい加減見づらいので平均出すと、
①29.7
②22.8
③1.9
④197.4
⑤22.5
ですね。わかっちゃいたけど④ェ…
意外なのが、たとえ1件ずつの処理でも、insertよりbatchInsertの方が速い…と言うほどではなくても、少なくとも同等であること。
書き方の統一を考えても、もしかして常にbatchInsert使った方がよいのでは…?
あとは、まとめての処理がめちゃくちゃ速いですね。
100件挿入が2ミリ秒程度で完了て。
並列処理に弱いとしても、データの参照・更新頻度によっては、こんだけ速けりゃカバーできそうな気もする。
日付の扱い方
まずは(SQLで言うところの)INSERT値やWHERE句など、日付をDBに送りつける場合。
PHPではユーザの入力値やなんやがあるので文字列で持ってることが多いんで、それを想定。
// 例によって$collectionはコレクションのやつ // 日付が入っている変数を $date_string = "2016-01-01 12:34:56"; // MongoDate型にキャストして $insert_list = array( 'date' => new MongoDate(strtotime($date_string)), ); // insertじゃ $collection->insert($insert_list);
まあ操作もめんどいんですが、DBを覗いてみると標準時で入ってるからキレそう。
(PHPを介してINSERTしたりSELECTしたりする分には意識しなくていいけど)
次は(SQLで言うところの)SELECT。
日付な検索条件を送るときはINSERTとかと方法は同じですが、取得結果をまた文字列に直すパターンを想定して。
// 例によって$collectionはコレクションのやつ // 検索条件設定(MongoDate型にキャスト) $from_date = new MongoDate(strtotime(date("Y-m-d") . " 00:00:00")); $to_date = new MongoDate(strtotime(date("Y-m-d") . " 23:59:59")); $where_list = array( '$and' => array( array('date' => array('$gte' => $from_date, '$lte' => $to_date)), ), ); // 検索実行(配列化して確保) $cursor = $collection->find($where_list); $data_list = iterator_to_array($cursor); // ここで$data_listを覗いてみると、日付(dateフィールド)の戻り値は // array('date' => MongoDateObject('sec' => 1234567890, 'usec' => 0)) // という形式になっていたので // secの部分をtime値として扱い文字列に直す foreach ($data_list as $key => $val) { $data_list[$key]['date_string'] = date("Y-m-d H:i:s", $val['date']->sec); }
めんどい。
とりあえずここまで
長くなってきたのでここらでpost
後からこっそり追記したりします