5.1 TD の高速化
TD で無駄の少ないクエリを書く場合, 特に気をつけるべき箇所は以下です.
WHERE
句でTD_TIME_RANGE()
を使用する- ソート処理を工夫する
- ユニークカウントを避ける
- 不要な列を使わない
は既に書いたとおりです. ログデータには全てタイムスタンプがついています. よって記録時刻で絞り込めば不要なレコードを読み込む必要がありません.
ソートは全てのレコードを参照するため, 特に分散処理を前提とした HIVE と相性が悪く, 処理に時間がかかりがちですが, 一方で工夫によって無駄な処理を減らす余地もあります. 例えば
id
列ごとにタイムスタンプtime
でソートしたいとします. 通常ならばORDER BY id, time
と書きたくなりますが, しかし実際には求める結果にはid
列をソートすることは必須ではありません. このような場合,DISTRIBUTED BY id SORT BY time
と書くことでid
のソート処理を省略できます. これは内部的には, レコードをid
ごとに reducer に割り当てる, という処理をしています. 同様の理由で, 中央値や分位点を計算する処理も分散処理と相性が悪いことを覚えておいてください.
同じく reducer への割り当てが原因で, GROUP BY で指定する列の順番もパフォーマンスに影響します. 濃度 (cardinality) の大きな列を先に持ってきたほうが速くなります. 例えば 「男女」と「ユーザーID」という列でグループ化したい場合, この順番よりも 「ユーザーID」「男女」のほうが速くなります. これは Presto でも同様に注意してください.
- ユニークカウントもソートも負荷の多く, 特に分散処理と相性が悪い操作です. 多くの SQL では
COUNT(DISCINT col)
のような書き方をしますが, Hive では遅くなる原因です. 先にDISTINCT
なテーブルをサブクエリやWITH
で作成したほうが reducer を効率よく使えます. また, Presto を使用する場合, 正確なユニークカウントでなくても良いのなら, 近似値を返すAPPROX_DISTINCT()
関数を使うことも考慮してください.
-- bad
SELECT COUNT(DISTINCT col)
FROM db.table ;
-- good
SELECT COUNT(1)
FROM (
SELECT DISTINCT col FROM db.table
) ;
- Presto は中間データをメモリで保持するため, 不要なデータは極力参照しないようにしてください. 例えば集計に不要な列はクエリの速い段階で捨ててしまいましょう.