Amazon Athenaでパーティション分割を試してみる
Athenaにおける悩み
AthenaでWAFやELBのログを分析する際、ログが格納されている一番上のパスを指定してテーブルを作成すると、含まれるデータの範囲を気にする必要がない反面、スキャンサイズが大きくなるし、クエリ時間も長くなります。
だからと言って見たい範囲のデータに絞って毎回テーブルを作成し直すのもめんどくさいです。
パーティション分割
そんな悩みを解消するのがデータのパーティション分割です。
クエリによってスキャンされるデータの量を制限できるようになるため、パフォーマンスが向上や、コストの削減が見込めます。
Apache Hive スタイルのパーティションの他に手動でパーティションを追加することで分割ができます。
AWSリソースのログ向けのテーブル作成方法
丁寧にAWS側でサンプルクエリが用意されています。
試してみる
WAFのログで実際にクエリを試してみようと思います。
テーブル作成
WAFのログ向けにパーティションを利用してテーブルを作成してみます。(2023年分のみ)
CREATE EXTERNAL TABLE `waflogs-2023`(
`timestamp` bigint,
`formatversion` int,
`webaclid` string,
`terminatingruleid` string,
`terminatingruletype` string,
`action` string,
`terminatingRuleMatchDetails` array < struct < conditionType: string, location: string, matchedData: array < string > > >,
`httpsourcename` string,
`httpsourceid` string,
`ruleGroupList` array < struct <
ruleGroupId: string,
terminatingRule: struct < ruleId: string, action: string >,
nonTerminatingMatchingRules: array < struct < action: string, ruleId: string > >,
excludedRules: array < struct < exclusionType: string, ruleId: string > >
> >,
`ratebasedrulelist` array< struct< ratebasedruleid:string, limitkey:string, maxrateallowed:int > >,
`nonterminatingmatchingrules` array< struct< ruleid:string, action:string > >,
`httprequest` struct<
clientip:string,
country:string,
headers:array< struct< name:string, value:string > >,
uri:string,
args:string,
httpversion:string,
httpmethod:string,
requestid:string
>
)
PARTITIONED BY (
`date` string
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://bucket/'
TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.date.type' = 'date',
'projection.date.range' = '2023/01/01,2023/12/31',
'projection.date.format' = 'yyyy/MM/dd',
'projection.date.interval' = '1',
'projection.date.interval.unit' = 'DAYS',
'storage.location.template' = 's3://bucket/${date}'
)
ちゃんとパーティション付きのテーブルができました!
クエリ実行
本当にスキャン範囲が制限できてクエリパフォーマンスが向上するのか、実際にクエリを実行して確認してみます。
パーティション分割したテーブル
SELECT
DATE_FORMAT(FROM_UNIXTIME(logs.timestamp/1000, 'Asia/Tokyo') ,'%Y-%m-%d %h:%i:%s') as JST
FROM
"default"."waflogs-2023" logs -- 2023年のデータが格納されたテーブル
WHERE
logs."date" = '2023/04/11'
キュー内の時間: 138 ミリ秒
実行時間: 6.387 秒
スキャンしたデータ: 86.07 MB
パーティション分割していないテーブル
SELECT
DATE_FORMAT(FROM_UNIXTIME(logs.timestamp/1000, 'Asia/Tokyo') ,'%Y-%m-%d %h:%i:%s') as JST
FROM
"default"."waflogs-2023-04" logs -- 2023年4月分のデータが格納されたテーブル
WHERE
DATE_FORMAT(FROM_UNIXTIME(logs.timestamp/1000, 'Asia/Tokyo') ,'%Y-%m-%d') = '2023-04-11'
キュー内の時間: 178 ミリ秒
実行時間: 13.601 秒
スキャンしたデータ: 1.74 GB
実行結果
実行時間 | スキャンしたデータ | |
---|---|---|
パーティション分割したテーブル | 6.387 秒 | 86.07 MB |
パーティション分割していないテーブル | 13.601 秒 | 1.74 GB |
スキャンされたレコードが1日分のデータになり、クエリのパフォーマンスが向上しました!
何度も数GBのクエリを実行すると実行時間が伸びたり、コストが心配になってきますが、パーティション列を条件に含めれば問題がなさそうです。
※パーティション列を条件に加えないと全検索になってしまうので、注意が必要です。途中でキャンセルしたとしても、キャンセルまでにスキャンされたデータには課金されてしまいます。
Q: Athena でのクエリが失敗した場合、課金されますか?
クエリ単位の料金設定では、クエリが失敗しても料金は発生しません。
Q: Athena はキャンセルされたクエリに対して課金されますか?
はい。クエリをキャンセルした場合、クエリをキャンセルした時点までにスキャンされたデータ量に対して課金されます。
https://aws.amazon.com/jp/athena/faqs/
まとめ
パーティション分割によってパフォーマンスの向上やコスト最適化できることがわかりました。
ただし、全件検索には気をつけましょう。
全件検索した際の影響軽減のためにデータの範囲はある程度絞っておく方が良いかもしれません。
次回はパーティション射影(パーティション管理の自動化)についても試してみようと思います。
Discussion