はじめに
昨年末のre:InventでAWS Clean Roomsというサービスが発表されました。
昨今、データクリーンルームという言葉が聞かれ始めたので、AWSはどういうサービスを提供するのか興味ありました。
そして先日プレビューが開始されました。
なので触ってみた内容や、所感などを書いていきます。
まだプレビュー版であり、私自身素人ですので、判断される際は他の情報と照らし合わせたり、自身で試用して確認してください。
サービスの概要
- 「コラボレーション」という場が作れます
- コラボレーションには、AWSアカウントが参加できます
- メンバーと呼ぶようです
- 最大5メンバー
- メンバーには2種類あります
- クエリを実行して結果を受け取ることができるメンバー
- 1メンバーのみ
- データを提供するメンバー
- 上記メンバー以外
- クエリを実行して結果を受け取ることができるメンバー
- 料金は、クエリ実行に対してのみ
- コラボレーション作成・維持にはかからない?
- 「どのデータを提供する?」の設定も対象外?
- 公式の料金説明:https://aws.amazon.com/jp/clean-rooms/pricing/
- データ提供用に別途リソースを作る必要がなさそう
- 専用のIAMロールは必要
- 見せるカラムを選択する
- 許可する集計の列と集計方法も選択する
使っていて、以下のようなイメージが浮かびました。
やってみた
AWSアカウントを2つ用意して、「受領企業」と「提供企業」として扱います。
コラボレーション作成
「受領企業」アカウントにて、コラボレーション作成をクリックします。
メンバーにアカウントを指定します。自アカウントのIDはデフォルトで入っています。
「メンバー能力」の箇所で、データを見せてもらうアカウントを指定します。今回は「受領企業」アカウントを指定します。
作成直後で、「提供企業」アカウントが招待を受領する前の状況は以下のようになっています。
招待を受ける
招待を受けたアカウント側では、先のコラボレーションが参加可能タブに表示されます。
招待を受けるには、メンバーシップを作成、から行います。
メンバーシップを作成されると、ステータスがアクティブになりました。
役割(メンバー能力)で、表示されるタブに違いがありました。
データ提供(Glueテーブル公開)
提供企業アカウント側でデータを用意し、受領企業アカウント側に公開していきます。
Glueテーブルを公開するので、関連するリソースをCloudFormationで作ります。
クリックで表示
データ格納バケット、Athena結果出力バケット、Athenaのワークグループ、Glueデータベースを作ります。昔のものを流用しているので、一部アウトプットが余計ですがご容赦ください。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Env:
Type: String
Resources:
RawDataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub raw-data-${AWS::AccountId}-${AWS::Region}-${Env}
BucketEncryption:
ServerSideEncryptionConfiguration:
-
ServerSideEncryptionByDefault:
SSEAlgorithm: 'AES256'
BucketKeyEnabled: false
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
AthenaQueryResultBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub query-result-${AWS::AccountId}-${AWS::Region}-${Env}
BucketEncryption:
ServerSideEncryptionConfiguration:
-
ServerSideEncryptionByDefault:
SSEAlgorithm: 'AES256'
BucketKeyEnabled: false
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
-
Id: 'auto-delete'
Status: 'Enabled'
ExpirationInDays: 7
AthenaWorkGroup:
Type: AWS::Athena::WorkGroup
Properties:
Name: !Sub athena-work-group-${Env}
RecursiveDeleteOption: true
WorkGroupConfiguration:
ResultConfiguration:
OutputLocation: !Sub s3://${AthenaQueryResultBucket}/data
EncryptionConfiguration:
EncryptionOption: 'SSE_S3'
EnforceWorkGroupConfiguration: true
PublishCloudWatchMetricsEnabled: true
GlueDatabase:
Type: AWS::Glue::Database
Properties:
CatalogId: !Ref AWS::AccountId
DatabaseInput:
Name: !Sub glue-database-${Env}
Outputs:
RawDataBucket:
Value: !Ref RawDataBucket
Export:
Name: !Sub "${Env}-RawDataBucket-Name"
AthenaQueryResultBucket:
Value: !Ref AthenaQueryResultBucket
Export:
Name: !Sub "${Env}-AthenaQueryResultBucket-Name"
AthenaWorkGroup:
Value: !Ref AthenaWorkGroup
Export:
Name: !Sub "${Env}-AthenaWorkGroup-Name"
GlueDatabase:
Value: !Ref GlueDatabase
Export:
Name: !Sub "${Env}-GlueDatabase-Name"
Athenaを作成します。先のCloudFormationで設定したパラメータと同じパラメータを指定してください。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Env:
Type: String
Resources:
ItemTable:
Type: AWS::Glue::Table
Properties:
CatalogId: !Ref AWS::AccountId
DatabaseName:
Fn::ImportValue:
!Sub "${Env}-GlueDatabase-Name"
TableInput:
Name: item
TableType: EXTERNAL_TABLE
Parameters:
skip.header.line.count: 1
has_encrypted_data: false
serialization.encoding: utf-8
EXTERNAL: true
StorageDescriptor:
OutputFormat: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Columns:
- Name: item_cd
Type: string
- Name: item_name
Type: string
- Name: store
Type: string
- Name: salse
Type: int
InputFormat: org.apache.hadoop.mapred.TextInputFormat
Location:
Fn::Join:
- ''
- - 's3://'
- Fn::ImportValue: !Sub "${Env}-RawDataBucket-Name"
- '/item'
SerdeInfo:
Parameters:
field.delim: ","
serialization.format: ","
SerializationLibrary: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
作成されたバケットに、以下のファイルを置きます。Athena作成時のロケーションに指定したitem
フォルダ以下に置くようにします。
item_cd,item_name,store,salse
AAAA,商品A,店舗1,1
AAAA,商品A,店舗2,10
AAAA,商品A,店舗2,100
BBBB,商品B,店舗1,1
BBBB,商品B,店舗2,10
CCCC,商品C,店舗1,1
設定すると分析ルールを設定するよう促されますので、設定します。
集約関数を設定します。数値項目(っぽい)「salse列に対しSUMを許可」で指定します。
結合コントロールで「はい」を指定することで、他テーブルと結合しなくても使えるようです。
集約制約なるものを設定します。(よくわからなかったので適当に設定しています。)
集約制約については、後で「こうじゃないかな・・・?」というのを説明します。
必要なロールは自動作成で作ってくれます。(ロール名は自分で指定可能なので、テーブル名や相手先など、わかりやすい名前を付けておくとよいと思います。)
受領企業アカウントに移り確認すると、関連付けを行ったテーブルに対してクエリ可能になりました。
クエリしてみる
受領企業アカウントに移り、クエリを実行しますが、最初にバケットを指定します。
試しに以下のクエリを実行します。
SELECT
"item"."store",
SUM("item"."salse")
FROM "item"
GROUP BY
"item"."store"
集約制約の影響
今度はitem_name
でグルーピングして検索してみます。
SELECT
"item"."item_name",
SUM("item"."salse")
FROM "item"
GROUP BY
"item"."item_name"
先ほどの制約を修正して、item_cd
からstore
に変更しました。編集するときはガイドフローは使えませんので、JSONを編集します。
そして同じSQLを実行してみます。
SELECT
"item"."item_name",
SUM("item"."salse")
FROM "item"
GROUP BY
"item"."item_name"
試しに以下のファイルをフォルダにいれて再実行すると、以下のような結果がかえってきます。
item_cd,item_name,store,salse
CCCC,商品C,店舗9,1000
集約制約の考察
これらのことから考えるに「グループ化した際に各グループで、集約制約で指定した列の一意の個数が最小値を超えている場合に、対象とする」となっているように見られました。
ケース1
最初に実行したクエリでは、sotre
でグループ化して、「item_cd
が2以上」という制約でした。
SELECT
"item"."store",
SUM("item"."salse")
FROM "item"
GROUP BY
"item"."store"
対象のデータを、store
とitem_cd
で一意にすると以下のようになり、各店舗ごとでitem_cd
の数が2以上になり、集計されたようです。
store,item_cd
店舗1,AAAA
店舗1,BBBB
店舗1,CCCC
店舗2,AAAA
店舗2,BBBB
ケース2
2番目に実行したクエリでは、item_name
でグループ化して、「item_cd
が2以上」という制約でした。
SELECT
"item"."item_name",
SUM("item"."salse")
FROM "item"
GROUP BY
"item"."item_name"
先ほどと同様に、item_name
とitem_cd
で一意にすると以下のようになり、「商品A」「商品B」「商品C」いずれも1つになってしまったので、対象から外れた、と考えられます。
item_name,item_cd
商品A,AAAA
商品B,BBBB
商品C,CCCC
ケース3
同じSQLに対して制約条件を変えて、item_name
でグループ化して、「store
が2以上」と設定するとどうでしょう。
item_name
とstore
で一意にすると以下のようになり、「商品C」が1つになってしまったので、対象から外れた、と考えられます。
item_name,store
商品A,店舗1
商品A,店舗2
商品B,店舗1
商品B,店舗2
商品C,店舗1
ケース4
その後、同じSQLで同じ制約条件、データに「商品Cに対して、店舗1以外のデータ」を追加した結果、「店舗C」も2以上になり集計対象になった、という動きをしていました。
item_name,store
商品A,店舗1
商品A,店舗2
商品B,店舗1
商品B,店舗2
商品C,店舗1
商品C,店舗9
おわりに
プレビュー版ですが、新サービスのAWS Clean Roomsを触ってみました。
提供側が(Glueテーブルを作ってあれば)わざわざ新規でデータを用意しなくても提供できる、というのはかなり良いかと思います。
別のデータと結合したり、別のタイプ「リスト」というのもありますが、今回はそこまでできませんでした。時間があったら見てみようと思います。