S3 SELECTのパターンとLambda実行時間を調べてみた(CSV、JSON、GZIP圧縮の有無)
はじめに
サーバーレス開発部の藤井元貴です。
S3のオブジェクトに対し、簡単なSQLを発行してデータ取得できる「S3 SELECT」があります。
このS3 SELECTについて、いくつかのパターンとLambda実行時間を調べてみました。
おすすめの方
- S3 SELECTに興味がある
- S3 SELECTを使用するLambdaの実行時間に興味がある
環境
項目 | バージョン |
---|---|
macOS | High Sierra 10.13.6 |
AWS CLI | aws-cli/1.16.89 Python/3.6.1 Darwin/17.7.0 botocore/1.12.79 |
Python | 3.6 |
使用データの例
CSV形式の例です。詳細は本記事の下部にあります。(JSONもそちらをどうぞ)
id,uuid 1,a70b64e7-b23d-4c10-9ad4-c90d27c9640f 2,1ce0c445-9586-4500-bfb3-23ea9908aed9
パターン&結果
No | 形式 | 圧縮 | ファイルサイズ(MB) | Lambda実行時間(ms) |
---|---|---|---|---|
1 | CSV | 無し | 213.5 | 4048.00 |
2 | CSV | GZIP | 115.7 | 4021.60 |
3 | JSON | 無し | 313.7 | 3687.14 |
4 | JSON | GZIP | 122.2 | 3509.75 |
- Lambdaのメモリ割り当ては、128MBです
- Lambdaの実行時間は、5回の平均です
- 1回目のコールドスタート含む
- 内訳は本記事の下部に記載
- レコード数は、500万件です
- シンプルなデータ(項目2個)を使用します
コメント
GZIP圧縮の有無
実行時間は、ほぼ同じとなりました。
S3 SELECTは下記に課金されるため、スキャンされるデータ量を減らすために「圧縮あり」のほうが良さそうです。(料金的に)
- S3 Select によって返されたデータ
- S3 Select によってスキャンされたデータ
CSV形式かJSON形式か
今回の場合は、JSON形式のほうが実行速度は有利ですね。
注意!!!
項目数やネスト数(JSONの場合)など、対象データの内容次第で、Lambda実行時間は大きく変わると思います。 やはり「やってみる」のが大切ですね。
以下、やってみたメモ
事前準備
CSVファイルの準備
ヘッダー有りのCSVファイルを準備します。レコード数は500万とします。項目数は2個とシンプルです。
id,uuid 1,a70b64e7-b23d-4c10-9ad4-c90d27c9640f 2,1ce0c445-9586-4500-bfb3-23ea9908aed9 3,148cc0e2-17b4-4b4b-aebc-481c56f37c8f 4,78c32510-fa93-4e35-b07e-cb1b58d22437 5,15cc096b-abfe-4e60-9424-08e14957b694 (略) 4999996,e6fb90e1-d7d4-4642-b3a5-7a9392a47045 4999997,b3006400-1dac-4423-8b6c-e12a1e2bb27e 4999998,2ec7aeb3-e2c4-4dc6-a3d6-a382ee0ed898 4999999,ed00ad80-5912-447b-bac4-aa8928c68d70 5000000,c4ba17be-16cb-4ede-9e84-2483c3746848
次のコードを用いてCSVファイルを作成しました。
import uuid with open('test.csv', 'w') as f: f.write('id,uuid\n') for index in range(5000000): f.write(f'{index+1},{uuid.uuid4()}\n')
JSONファイルの準備
S3 SELECTで利用できるJSON形式については、下記をご覧ください。
さきほど作成したCSVファイルをJSON Lines形式
に変換しました。
{"id": "1", "uuid": "a70b64e7-b23d-4c10-9ad4-c90d27c9640f"} {"id": "2", "uuid": "1ce0c445-9586-4500-bfb3-23ea9908aed9"} {"id": "3", "uuid": "148cc0e2-17b4-4b4b-aebc-481c56f37c8f"} {"id": "4", "uuid": "78c32510-fa93-4e35-b07e-cb1b58d22437"} {"id": "5", "uuid": "15cc096b-abfe-4e60-9424-08e14957b694"} (略) {"id": "4999996", "uuid": "e6fb90e1-d7d4-4642-b3a5-7a9392a47045"} {"id": "4999997", "uuid": "b3006400-1dac-4423-8b6c-e12a1e2bb27e"} {"id": "4999998", "uuid": "2ec7aeb3-e2c4-4dc6-a3d6-a382ee0ed898"} {"id": "4999999", "uuid": "ed00ad80-5912-447b-bac4-aa8928c68d70"} {"id": "5000000", "uuid": "c4ba17be-16cb-4ede-9e84-2483c3746848"}
変換に使用したコードです。
import csv import json csv_file = open('test.csv', 'r') json_file = open('test.json', 'w') reader = csv.DictReader(csv_file) for row in reader: json.dump(row, json_file) json_file.write("\n") json_file.close() csv_file.close()
GZIP圧縮
作成した2つのファイル(CSVとJSON)をGZIP圧縮します。
$ gzip -c test.csv > test.csv.gz $ gzip -c test.json > test.json.gz
S3にアップロード
バケット作成します。ある場合はSkipです。
$ aws s3 mb s3://cm-fujii.genki-test make_bucket: cm-fujii.genki-test
アップロードします。
$ aws s3 cp test.csv s3://cm-fujii.genki-test/s3-select/test.csv $ aws s3 cp test.json s3://cm-fujii.genki-test/s3-select/test.json $ aws s3 cp test.csv.gz s3://cm-fujii.genki-test/s3-select/test.csv.gz $ aws s3 cp test.json.gz s3://cm-fujii.genki-test/s3-select/test.json.gz
Lambdaの準備
AWS SAMなどを使わず、直接作成しました。
Lambda実行時のロールにAmazonS3ReadOnlyAccess
ポリシーをアタッチします。
こちらによると、GetObject
があればOKです。
CSV形式、圧縮なし
import boto3 BUCKET_NAME = 'cm-fujii.genki-test' OBJECT_KEY = 's3-select/test.csv' COMPRESSION_TYPE = 'NONE' def lambda_handler(event, context): query = 'SELECT * FROM S3Object s WHERE s.id=\'3\' or s.id=\'4999998\'' s3 = boto3.client('s3') # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.select_object_content response = s3.select_object_content( Bucket=BUCKET_NAME, Key=OBJECT_KEY, ExpressionType='SQL', Expression=query, InputSerialization={ 'CSV': { 'FileHeaderInfo': 'USE', 'RecordDelimiter': '\n', 'FieldDelimiter': ',', }, 'CompressionType': COMPRESSION_TYPE, }, OutputSerialization={ 'JSON': { 'RecordDelimiter': '\n' } } ) # 結果を表示 # S3 SELECTの戻り値は EventStream のためループで取り出す # https://botocore.amazonaws.com/v1/documentation/api/latest/reference/eventstream.html for payload in response['Payload']: if 'Records' in payload: records = payload['Records']['Payload'].decode('utf-8') print(records)
CSV形式、圧縮あり
設定の変更のみです。
BUCKET_NAME = 'cm-fujii.genki-test' OBJECT_KEY = 's3-select/test.csv.gz' COMPRESSION_TYPE = 'GZIP'
JSON形式、圧縮なし
InputSerialization
をJSONに合わせて変更しています。
import boto3 BUCKET_NAME = 'cm-fujii.genki-test' OBJECT_KEY = 's3-select/test.json' COMPRESSION_TYPE = 'NONE' def lambda_handler(event, context): query = 'SELECT * FROM S3Object s WHERE s.id=\'3\' or s.id=\'4999998\'' s3 = boto3.client('s3') # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.select_object_content response = s3.select_object_content( Bucket=BUCKET_NAME, Key=OBJECT_KEY, ExpressionType='SQL', Expression=query, InputSerialization={ 'JSON': { 'Type': 'Lines', }, 'CompressionType': COMPRESSION_TYPE, }, OutputSerialization={ 'JSON': { 'RecordDelimiter': '\n' } } ) # 結果を表示 # S3 SELECTの戻り値は EventStream のためループで取り出す # https://botocore.amazonaws.com/v1/documentation/api/latest/reference/eventstream.html for payload in response['Payload']: if 'Records' in payload: records = payload['Records']['Payload'].decode('utf-8') print(records)
JSON形式、圧縮あり
設定の変更のみです。
BUCKET_NAME = 'cm-fujii.genki-test' OBJECT_KEY = 's3-select/test.json.gz' COMPRESSION_TYPE = 'GZIP'
Lambda実行結果
結果は共通なので、これだけ載せておきます。バッチリ取得できてますね!
{"id":"3","uuid":"148cc0e2-17b4-4b4b-aebc-481c56f37c8f"} {"id":"4999998","uuid":"2ec7aeb3-e2c4-4dc6-a3d6-a382ee0ed898"}
Lambda実行時間(CSV形式)
回数 | 圧縮なし(ms) | 圧縮あり(ms) |
---|---|---|
1 | 5179.60 | 5056.94 |
2 | 4175.78 | 4293.95 |
3 | 3806.32 | 3560.01 |
4 | 3199.92 | 3468.49 |
5 | 3878.37 | 3728.59 |
平均 | 4048.00 | 4021.60 |
Lambda実行時間(JSON形式)
回数 | 圧縮なし(ms) | 圧縮あり(ms) |
---|---|---|
1 | 6049.82 | 5204.98 |
2 | 3688.56 | 3383.22 |
3 | 2906.65 | 3001.53 |
4 | 2829.20 | 3208.97 |
5 | 2961.49 | 2750.11 |
平均 | 3687.14 | 3509.76 |