[AWS Glue]データカタログに作成されたパーティションをAWS CLIで削除してみた

2021.01.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部の若槻です。

AWS Glueでは、パーティション分割を行うことによりデータの整理や効率的なクエリ実行を行うことが可能です。

このパーティションですが、開発時の検証のために作成済みのものを削除したい時があります。

そこで今回は、AWS Glueのデータカタログに作成されたパーティションをAWS CLIで削除する方法を確認してみました。

やってみた

ここでは、「AWS CLIのglueコマンド」と「Athenaでのクエリ実行」の2通りの方法について確認してみます。

最初に以降のコマンド実行で使用する変数を定義しておきます。

% AWS_REGION=ap-northeast-1
% ACCOUNT_ID=$(aws sts get-caller-identity | jq -r ".Account")
% RAW_DATA_BUCKET=s3://devices-raw-data-${ACCOUNT_ID}-${AWS_REGION}
% GLUE_DATABASE_NAME=devices_data_analystics
% GLUE_TABLE_NAME=devices_raw_data
% ATHENA_WORK_GROUP_NAME=devices-data-analytics

環境構築

CloudFormationスタック

CloudFormationスタックのテンプレートです。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  DevicesRawDataBucket:
    Type: AWS::S3::Bucket
    Properties: 
      BucketName: !Sub devices-raw-data-${AWS::AccountId}-${AWS::Region}

  DevicesDataAnalyticsAthenaWorkGroup:
    Type: AWS::Athena::WorkGroup
    Properties:
      Name: devices-data-analytics
      WorkGroupConfiguration:
        ResultConfiguration:
          OutputLocation: !Sub s3://${DevicesRawDataBucket}/query-result
        EnforceWorkGroupConfiguration: true
        PublishCloudWatchMetricsEnabled: true

  DevicesDataAnalyticsGlueDatabase:
    Type: AWS::Glue::Database
    Properties: 
      CatalogId: !Ref AWS::AccountId
      DatabaseInput:
        Name: devices_data_analystics

  DevicesRawDataGlueTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId: !Ref AWS::AccountId
      DatabaseName: !Ref DevicesDataAnalyticsGlueDatabase
      TableInput:
        Name: devices_raw_data
        TableType: EXTERNAL_TABLE
        Parameters:
          has_encrypted_data: false
          serialization.encoding: utf-8
          EXTERNAL: true
        StorageDescriptor:
          OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
          Columns:
            - Name: device_id
              Type: string
            - Name: timestamp
              Type: bigint
            - Name: state
              Type: boolean
          InputFormat: org.apache.hadoop.mapred.TextInputFormat
          Location: !Sub s3://${DevicesRawDataBucket}/raw-data
          SerdeInfo:
            Parameters:
              paths: "device_id, timestamp, state"
            SerializationLibrary: org.apache.hive.hcatalog.data.JsonSerDe
        PartitionKeys:
          - Name: year
            Type: string
          - Name: month
            Type: string
          - Name: day
            Type: string

リソース定義DevicesRawDataGlueTableで、yearmonthdayの3つのパーティションキーを持ったGlueテーブルを作成しています。

スタックをデプロイします。

% aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name devices-data-analytics-stack \
  --capabilities CAPABILITY_NAMED_IAM \
  --no-fail-on-empty-changeset

データの準備

データソースの場所となるS3バケットのパスに3種類のパーティションパスのオブジェクトを作成します。

raw-data.json

{"device_id": "3ff9c44a", "timestamp": 1609348014, "state": true}
% aws s3 cp raw-data.json \
	${RAW_DATA_BUCKET}/raw-data/year=2021/month=01/day=13/raw-data.json
%	aws s3 cp raw-data.json \
	${RAW_DATA_BUCKET}/raw-data/year=2021/month=01/day=14/raw-data.json
% aws s3 cp raw-data.json \
	${RAW_DATA_BUCKET}/raw-data/year=2021/month=01/day=15/raw-data.json

共通スクリプトの用意

パーティション一括作成および一括取得の簡易化のため、共通スクリプトをMakefileで用意しておきます。

Makefile

export AWS_REGION=ap-northeast-1
export ACCOUNT_ID=$(shell aws sts get-caller-identity | jq -r ".Account")
export GLUE_DATABASE_NAME=devices_data_analystics
export GLUE_TABLE_NAME=devices_raw_data
export ATHENA_WORK_GROUP_NAME=devices-data-analytics

#パーティション一括作成
create-partitions:
	aws athena start-query-execution \
		--query-string "MSCK REPAIR TABLE $(GLUE_TABLE_NAME)" \
		--work-group $(ATHENA_WORK_GROUP_NAME) \
		--query-execution-context Database=$(GLUE_DATABASE_NAME),Catalog=AwsDataCatalog

#パーティション一括取得
get-partitions:
	partitions=$$(aws glue get-partitions \
		--database-name $(GLUE_DATABASE_NAME) \
		--table-name $(GLUE_TABLE_NAME) \
		--query Partitions\
	);\
	len=$$(echo $$partitions | jq length);\
	for i in $$(seq 0 $$(($$len - 1)) );\
		do echo $$partitions | jq ".[$$i].Values";\
	done

AWS CLIのglueコマンドによる方法

一つのパーティションの削除

パーティションを一つ削除する場合はdelete-partitionコマンドで可能です。

パーティションを一括作成した上で、year=2021,month=01,day=13のパーティションを削除してみます。

% make create-partitions
% aws glue delete-partition \
  --database-name ${GLUE_DATABASE_NAME} \
  --table-name ${GLUE_TABLE_NAME} \
  --partition-values 2021 01 13

パーティションを一括取得してみると、day=13のパーティションだけ削除されて、day=14およびday=15のパーティションが残っていますね。

% make get-partitions
[
  "2021",
  "01",
  "14"
]
[
  "2021",
  "01",
  "15"
]

複数のパーティションの削除

複数のパーティションの一括削除はbatch-delete-partitionコマンドで可能です。

パーティションを一括作成し、batch-delete-partitionday=13およびday=14を指定して実行してみます。({"Errors": [], ...}のような実行結果が表示されますが無視してください)

% make create-partitions
% aws glue batch-delete-partition \
  --database-name ${GLUE_DATABASE_NAME} \
  --table-name ${GLUE_TABLE_NAME} \
  --partitions-to-delete Values=2021,01,13 Values=2021,01,14

パーティションを一括取得してみると、day=13およびday=14のパーティションが削除されて、day=15のパーティションだけ残っていますね。

% make get-partitions
[
  "2021",
  "01",
  "15"
]

Athenaでのクエリ実行による方法

Athenaでのクエリ実行でパーティションを削除したい場合は、ALTER TABLE DROP PARTITIONクエリを使えば一つまたは複数の削除のいずれにも対応可能です。

一つのパーティションの削除

パーティションを一括作成して、ALTER TABLE DROP PARTITIONクエリでyear=2021,month=01,day=13を指定して削除してみます。

% make create-partitions
% queryString="ALTER TABLE ${GLUE_TABLE_NAME} DROP PARTITION (year='2021', month='01', day='13')"
% aws athena start-query-execution \
  --query-string ${queryString} \
  --work-group ${ATHENA_WORK_GROUP_NAME} \
  --query-execution-context Database=${GLUE_DATABASE_NAME},Catalog=AwsDataCatalog

パーティションを一括取得してみると、day=13のパーティションだけ削除されて、day=14およびday=15のパーティションが残っていますね。

% make get-partitions
[
  "2021",
  "01",
  "14"
]
[
  "2021",
  "01",
  "15"
]

複数のパーティションの削除

パーティションを一括作成して、ALTER TABLE DROP PARTITIONクエリでday=13およびday=14のパーティションを指定して削除してみます。

% make create-partitions
% queryString="ALTER TABLE ${GLUE_TABLE_NAME} DROP PARTITION (year='2021', month='01', day='13'), PARTITION (year='2021', month='01', day='14')"
% aws athena start-query-execution \
  --query-string ${queryString} \
  --work-group ${ATHENA_WORK_GROUP_NAME} \
  --query-execution-context Database=${GLUE_DATABASE_NAME},Catalog=AwsDataCatalog

パーティションを一括取得してみると、day=13およびday=14のパーティションが削除されて、day=15のパーティションだけ残っていますね。

% make get-partitions
[
  "2021",
  "01",
  "15"
]

すべてのパーティションを一括削除できないの?

できなさそうです。ただし近い方法としてAthenaによるALTER TABLE DROP PARTITIONクエリ実行であれば、一番カーディナリティが低いパーティションキーをすべて指定して削除する方法が良さそうでした。これなら今回のデータであれば(year='2021')を指定するだけで一括削除できます。

% make create-partitions
% queryString="ALTER TABLE ${GLUE_TABLE_NAME} DROP PARTITION (year='2021')"
% aws athena start-query-execution \
  --query-string ${queryString} \
  --work-group ${ATHENA_WORK_GROUP_NAME} \
  --query-execution-context Database=${GLUE_DATABASE_NAME},Catalog=AwsDataCatalog

また、出来そうで出来なかったクエリへの指定として、(year<>'')(year is not null)(year>'0')(year!='')などがありました(実行してもエラーとなる)。下記によるとHive標準では使えるようですがAthenaでは使えないようです。

glueコマンドの場合は、get-partitionsでの取得結果をShellでゴニョゴニョしてbatch-delete-partitionに渡すしか無さそうです。下記が参考になります。

場合によってはGlueテーブルを作り直した方が手っ取り早いかも知れませんね。

おわりに

AWS Glueのデータカタログに作成されたパーティションをAWS CLIで削除する方法を確認してみました。

とりあえず今度から一括削除したい時はALTER TABLE DROP PARTITIONクエリでカーディナリティが低いパーティションキーを指定する方法を使いたいと思います。

参考

以上