コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
クエリプランナの役割
遅いクエリは、心当たりでは見つからない
「最近ページが重い」と言われたとき、いちばんやりがちなのは、見た目が複雑なクエリから疑うことです。だいたい外れます。効いているのは、1 回 0.3 秒でも 1 リクエストに 40 回呼ばれている単純な SELECT だったりします。
先に測ります。PostgreSQL なら pg_stat_statements、MySQL ならスロークエリログです。並べる基準は 1 回あたりの時間ではなく、合計時間にします。
SQL クエリ
SELECT calls, total_exec_time, mean_exec_time, query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;上位 3 本で全体の何割を占めているかを見れば、直す対象はたいてい決まります。ここを飛ばして改善に入ると、速くしたはずなのに体感が変わらない、という結果になります。
同じ SQL が、速かったり遅かったりする
対象が決まったら次は原因ですが、ここで戸惑うのは、SQL に遅くなる要素が書かれていないことです。
SELECT * FROM orders WHERE user_id = 100 ORDER BY created_at LIMIT 10 が指定しているのは、欲しい結果だけです。どの索引を使うか、どこで並べ替えるか、どのテーブルから読み始めるかは 1 文字も書かれていません。それを決めるのがクエリプランナで、同じ SQL でも次のような複数の手順が候補になります。
user_idで絞ってから、集めた行をメモリ上で並べ替えるcreated_atの順に読みながらuser_idが 100 の行を拾い、10 件たまったら止める(user_id, created_at)の索引を使い、絞り込みと並べ替えを同時に済ませる
どれが速いかは、user_id = 100 の行が 3 件なのか 30 万件なのかで変わります。プランナはテーブルの行数や値の散らばり方の記録からそれを見積もり、いちばん安く済みそうな手順を選びます。
だから、テーブルが育った日を境に、昨日まで速かったクエリが急に遅くなることがあります。SQL は 1 文字も変わっていなくても、選ばれた手順が変わったからです。
選ばれた手順を見るまでは、直しようがない
見積もりが当たっている保証はありません。記録が古ければ外れますし、country と city のように片方が決まればもう片方もほぼ決まる列の組み合わせでも外れます。
ですから、遅い理由を推測で決めないことです。EXPLAIN を頭に付けて実行すると、プランナが選んだ手順が表示されます。
SQL クエリ
EXPLAIN SELECT * FROM orders WHERE user_id = 100 ORDER BY created_at LIMIT 10;索引を足すのも、SQL を書き換えるのも、この出力を見てからです。手を入れた後にもう一度同じ出力を取れば、狙いどおり手順が変わったのかまで確認できます。