Athena のパーティション設定2種

こんにちは、masm11 です。 今回は AWS Athena のパーティションの設定方法について説明したいと思います。

まず Athena とは

Cloudwatch Logs やその他の場所でログが発生すると、 手軽に検索したくなりますよね。

そういう場合、S3 に置いておくと Athena で SQL を使って検索できます。

発生したログはただのテキストだと思いますので、なんとかして以下のように JSON にしておきます。Cloudwatch Logs に溜まっているなら、Firehose から Lambda を使うのが良いでしょう。

{"timestamp":"2021-04-24T22:50:45","message":"test log 1"}
{"timestamp":"2021-04-24T22:51:00","message":"test log 2"}

こういうファイルをたくさん S3 に溜めておきます。

Athena では以下のように設定します。

CREATE EXTERNAL TABLE logs (
  timestamp string,
  message   string
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://bucket/logs';

こうすると、SQL を使って検索できます。

SELECT * FROM logs WHERE message LIKE 'test log%';

ただし、ここで重大な問題があります。この SQL でスキャンしたデータ量で 料金が発生するのです。なんとかしてスキャンする量を絞る必要があります。

そこでパーティションの出番です。

設定方法その1

S3 バケットの中を以下のように構造化しておきます。 Hive 形式と呼ばれるものです。

s3://bucket/logs/year=YYYY/month=MM/day=DD/file.ext

year= 等はそのままプレフィックスに含めます。YYYY 等は 数字に置き換えます。つまり、プレフィックスには year=2021 等という 文字列が含まれるわけですね。

そして、CREATE EXTERNAL TABLE は以下のように実行します。

CREATE EXTERNAL TABLE logs (
  timestamp string,
  message   string
)
PARTITIONED BY (year STRING, month STRING, day STRING)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://bucket/logs';

その後、

MSCK REPAIR TABLE logs;

を実行します。これで S3 のファイル名がスキャンされて、パーティションの 一覧が作成されます。この時には料金はかかりません。

これで準備ができました。あとは SELECT するだけです。

SELECT * FROM logs WHERE year = '2021' AND month = '04' AND day = '24' AND message LIKE 'test log%';

こうすると、2021-04-24 のログだけがスキャンされます。

ただし、プレフィックスが増えた時には MSCK REPAIR TABLE をしてやる 必要があります。

MSCK REPAIR TABLE だと、ファイル数が増えてくると時間がかかりますので、 代わりに ALTER TABLE ADD PARTITION を使っても良いでしょう。 その場合は、プレフィックスが不要になった場合は ALTER TABLE DROP PARTITION をする必要があります。しなくてもエラーは発生しませんが、 パーティションが増えると検索が遅くなります。

設定方法その2

設定方法その1は、パーティションのメンテが手間なんですよね。 そこで2つめの方法です。

s3://bucket/logs/YYYY/MM/DD/file.ext

year= 等がなくなり、シンプルになりましたね。

CREATE EXTERNAL TABLE は以下のようになります。

CREATE EXTERNAL TABLE logs (
  timestamp string,
  message   string
)
PARTITIONED BY (
  year INT,
  month INT,
  day INT
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://bucket/logs'
TBLPROPERTIES (
  'projection.enabled' = 'true',

  'projection.year.type' = 'integer',
  'projection.year.range' = '2021,2100',
  'projection.year.digits' = '4',

  'projection.month.type' = 'integer',
  'projection.month.range' = '1,12',
  'projection.month.digits' = '2',

  'projection.day.type' = 'integer',
  'projection.day.range' = '1,31'
  'projection.day.digits' = '2',

  'storage.location.template' = 's3://bucket/logs/${year}/${month}/${day}'
);

随分長くなりました。

'projection.day.type' = 'integer',
'projection.day.range' = '1,31'
'projection.day.digits' = '2',

day は整数で、1〜31 で、2桁であることを宣言しています。桁数が足りなければ 0 でパディングされます。

準備はこれで終わりです。検索はその1とだいたい同じですが、年月日は整数になります。

SELECT * FROM logs WHERE year = 2021 AND month = 4 AND day = 24 AND message LIKE 'test log%';

そしてこちらの方法はパーティションのメンテは不要です。

ただし、パーティションのキーはあらかじめ範囲を決めておく必要があります。つまり、

'projection.year.range' = '2021,∞',

なんて指定はできません。

まとめ

Athena のパーティションの設定方法を2つ紹介しました。 特に理由がなければ後者で良いと思います。

インゲージではエンジニアを募集しています。詳細は以下のページへ!

https://ingage.co.jp/recruit/