5.1 TD の高速化

TD で無駄の少ないクエリを書く場合, 特に気をつけるべき箇所は以下です.

  1. WHERE 句で TD_TIME_RANGE() を使用する
  2. ソート処理を工夫する
  3. ユニークカウントを避ける
  4. 不要な列を使わない
  1. は既に書いたとおりです. ログデータには全てタイムスタンプがついています. よって記録時刻で絞り込めば不要なレコードを読み込む必要がありません.

  2. ソートは全てのレコードを参照するため, 特に分散処理を前提とした HIVE と相性が悪く, 処理に時間がかかりがちですが, 一方で工夫によって無駄な処理を減らす余地もあります. 例えば idごとにタイムスタンプ time でソートしたいとします. 通常ならば ORDER BY id, time と書きたくなりますが, しかし実際には求める結果には id 列をソートすることは必須ではありません. このような場合, DISTRIBUTED BY id SORT BY time と書くことで id のソート処理を省略できます. これは内部的には, レコードを id ごとに reducer に割り当てる, という処理をしています. 同様の理由で, 中央値や分位点を計算する処理も分散処理と相性が悪いことを覚えておいてください.

同じく reducer への割り当てが原因で, GROUP BY で指定する列の順番もパフォーマンスに影響します. 濃度 (cardinality) の大きな列を先に持ってきたほうが速くなります. 例えば 「男女」と「ユーザーID」という列でグループ化したい場合, この順番よりも 「ユーザーID」「男女」のほうが速くなります. これは Presto でも同様に注意してください.

  1. ユニークカウントもソートも負荷の多く, 特に分散処理と相性が悪い操作です. 多くの 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
) ;
  1. Presto は中間データをメモリで保持するため, 不要なデータは極力参照しないようにしてください. 例えば集計に不要な列はクエリの速い段階で捨ててしまいましょう.