MySQLで複数のクエリを取り出すには、次のように、クエリを複数回発行する方法と、条件節(WHERE節)に複数の条件を指定する2つの方法があります。
- SELECT * FROM table WHERE id=?
これを複数回 - SELECT * FROM table WHERE id=? OR id=? OR id=? (以下 OR id=?が続く…)
これを一回
クエリを複数回発行するよりも、一回で済ませた方が早そうと推測出来ますが、さて、どれだけパフォーマンスが改善されるのでしょうか。
実験してみました。
実験
mysqliパッケージを使用。プリペアドステートメントを使用します。
前述の2つのSQL文をそれぞれに用いて、42個のレコードが入っているテーブルから、主キーを指定して42個のレコードを取り出し、かかった時間を計測します。
時間の計測は100回行い、平均値を比較します。
Macbook pro early 2015のMAMPローカル環境で実行します。(そのためSSDアクセスは早いです。)
$time = microtime(true);
foreach($ids as $id){
$db_cont->db_get_item($id);
}
echo "time for 42 queries of getting 1 record: ", microtime(true) - $time, "<br>", PHP_EOL;
$time = microtime(true);
$db_cont->db_get_items($ids));
echo "time for 1 query of getting 42 records: ", microtime(true) - $time, "<br>", PHP_EOL;
関数類(一部ユーザ関数を含む)
function db_get_item($id){
$sql = "SELECT * FROM items WHERE id=?;";
$vals = array("s", $id);
return $this->db->queryBind($sql, $vals);
}
function db_get_items($ids){
$sql = "SELECT * FROM items WHERE ".
implode(' OR ', array_fill(0, count($ids), 'id=?'));
$vals[] = str_repeat('s', count($ids));
$vals = array_merge($vals, $ids);
return $this->db->queryBind($sql, $vals);
}
結果
実行した結果、次のようになりました。
- time for 42 queries of getting 1 record: 0.0167034697533 sec
- time for 1 query of getting 42 records : 0.00169276952744 sec
この条件下では、0.0167/0.00169 ~ 10 から、一つのクエリで取り出したほうが 10倍早かったことがわかります。
考察
1. の結果から、1レコード取り出しにかかる時間は1クエリあたり、0.40 msです。
クエリ発行のオーバーヘッドがこの時間のどれだけを占めているか、この結果から推測してみます。
クエリのオーバーヘッドをq、レコード検索にかかる時間をsとして、次の関係が成り立つと仮定します。
- 0.40 ms = q + s * 1
- 1.7 ms = q + s * 42
これを解くと、次のようになります。
- q = 0.37 ms
- s = 0.032 ms
クエリにかかる時間は、0.37 msとなり、サーチにかかる時間の12倍です。サーチにかかる時間はほぼ無視できそうです。
100クエリを一回にまとめて発行するような前出のような関数を作ると、およそ37 ms早くなります。
37 ms程度がネックになってくるシステムで、100クエリ程度を発行している場合は、クエリをまとめると、MySQLへのアクセスによる遅延を抑える効果が期待できるということになります。
ただサーチにかかる時間は、検索条件に依存することはお忘れなく。
まとめ
MySQLで主キーを指定してレコードを取り出してみたところ、クエリー発行のオーバーヘッドは0.37 ms/query でした。環境に依存しますが、本実験条件下では、100クエリを一つにまとめると、37 ms程度の高速化が望めることになります。