LoginSignup
3
5

More than 5 years have passed since last update.

TD_SESSIONIZEを使うときはサブクエリを使え

Last updated at Posted at 2016-10-16

Webアクセスログ解析に超絶便利なTreasureDataのHive UDFのTD_SESSIONZEですが、使用する際には以下の前提があるので注意すること。
HiveQLがエラーになるわけではないが、狙ったようにセッションIDが付与されないので誤った集計結果になってしまいます。

  • 引数に渡すカラムについてDISTRIBUTE BYSORT BYするサブクエリの直後に使用する

TD_SESSIONIZEとは

同一アクセスと判断できるログに一意なセッションIDを付与してくれる関数。

string TD_SESSIONIZE(int/long unix_timestamp, int timeout, string sessionize_by)
  • 第一引数:UNIX時刻
  • 第二引数:同一セッションと判断する時間の範囲(単位は秒)
  • 第三引数:同一セッションと判断するフィールド

公式ドキュメント

具体例

サンプルで用意されているsample_datasetsのwww_accessテーブルを使って、
同じホスト(格納されているのはIPアドレス)からの1時間以内のアクセスを同じセッションとみなしたい場合、以下のように使います。

正しい使い方

SELECT 
  TD_SESSIONIZE(time,3600,host) AS session_id,
  TD_TIME_FORMAT(time,'yyyy-MM-dd HH:mm:ss','JST') AS access_time,
  host,
  path
FROM (
    SELECT 
      time,
      host,
      path
    FROM
      www_access
      DISTRIBUTE BY host
      SORT BY host,time
  ) t

結果には以下のようにセッションIDが付与されます。

session_id access_time host path
bb368f01-9198-4718-9df3-51baeff9fce1 2014-10-04 05:39:00 100.111.144.44 /item/computers/4323
bb368f01-9198-4718-9df3-51baeff9fce1 2014-10-04 06:11:08 100.111.144.44 /item/software/759

ここで、重要なのが、
* TD_SESSIONIZEの第三引数に使いたいフィールドでDISTRIBUTE BY
* TD_SESSIONIZEの第三引数、第一引数に使いたいフィールドでSORT BY
をサブクエリで実行した後にTD_SESSIONIZEを計算させること。
これは必須条件です。

誤った使い方

以下のようにTD_SESSIONIZEDISTRIBUTE BYSORT BYと同じレベルで実行するのはダメです。

SELECT 
  TD_SESSIONIZE(
    time,
    3600,
    host
  ) AS session_id,
  TD_TIME_FORMAT(time,
    'yyyy-MM-dd HH:mm:ss',
    'JST') AS access_time,
  host,
  path,
  time
FROM www_access DISTRIBUTE BY host SORT BY host,time

HiveQLはエラーを出さずに正常終了するのですが、結果として

session_id access_time host path
0f760fbe-4e7b-411a-b82d-4e44c0fd8cd8 2014-10-04 05:39:00 100.111.144.44 /item/computers/4323
c2700830-f6e7-40af-9c78-4bc72eebba64 2014-10-04 06:11:08 100.111.144.44 /item/software/759

のように、同じセッションとみなしてほしい(=同じホストで1時間以内のアクセス)レコードに異なるセッションIDが付与されてしまいます。

考察

落ち着いて考えると、それはそうだよなぁという感じ。
DISTRIBUTE BYSORT BYもMapReduceにおいてReducerに集めるカラムのキー範囲や順番を制御するものです。
TD_SESSIONIZEを実装するなら、事前にセッションを判定するキーが一箇所に集約されていて、しかも時間の順番にソートされている前提で、上から順番にレコードを読んでIDを付与していくのが一番楽です。
なので、一度MapReduceでその条件でレコードをグループ分けかつソートし、2段目のMapReduceのMapperでそれぞれTD_SESSIONIZEしてもらうのが自然。

辛いのは、ドキュメントの説明が

Similiar to TD_X_RANK, you need to ‘CLUSTER BY’ or ‘DISTRIBUTE BY’ the sessionize_by field, and sort by the unix_timestamp field.

となっていて、一見するとこの前提がわかりづらいこと。
Similar to TD_X_RANKとあるのでそちらの説明を見ると、

You need to ‘CLUSTER BY’ or ‘ORDER BY’ within a sub query to use this feature.

とあるので、霊感で気づくのが要求されます。。。

2016/10/17 追記

TreasureDataのサポート経由で報告したところ、すぐにドキュメントを修正してくれました!

Similiar to TD_X_RANK, you need to CLUSTER BY or DISTRIBUTE BY the sessionize_by field, and SORT BY the unix_timestamp field within a sub query to use this feature.

まとめ

超絶便利な関数なので上記の点に気をつけて使いまくりましょう。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5