본문 바로가기
자습

AWS S3-Select

by litaro 2019. 7. 20.

https://medium.com/@iamkanikamodi/write-a-sample-s3-select-lambda-function-in-aws-f386b48d7044

 

Write a sample S3-Select Lambda Function in AWS - Kanika Modi - Medium

What is S3-Select?

medium.com

S3에 대해 내가 아는 것은 다양한 컨텐츠를 저장할 수 있는 객체 스토리지였고, 주로 사용한 목적은

1. 로그 통계 파일 저장

2. 이미지 저장 : url 로 뽑기 위해~

3. static web resource (html, js, css..) 저장 

였다.

 

S3가 비용이 상대적으로 저렴해서 S3에서 DB를 쌓으려는 도전을 주변에서 하는것을 봤는데 결국 파일을 S3에서 들고와서 처리해야해서 파일 크기에 따른 성능 문제로 RDB나 DynamoDB로 가는것을 봤다.

 

그러다가 위의 글을 보고 S3로 단순한 SQL Query가 가능하다는 사실을 알게 되었다.

원래 있던 기능이었던것 같은데 나만 몰랐던...그래도 위안은 나름 최근?인 2018년 4월에 상용버전이 되었다는것...

https://aws.amazon.com/about-aws/whats-new/2018/04/amazon-s3-select-is-now-generally-available/

 

Amazon S3 Select, 상용 버전으로 사용 가능

지금부터, 모든 고객이 Amazon S3 Select를 사용할 수 있습니다. S3 Select는 객체에서 필요한 데이터만 가져오도록 고안된 새로운 Amazon S3 기능이며, S3에서 데이터에 액세스해야 하는 애플리케이션의 성능을 획기적으로 개선하고 비용을 절감할 수 있도록 해줍니다. 

aws.amazon.com

 

S3-Select를 활용하면 전체 파일을 들고와서 처리할 필요 없이 우리가 원하는 정보만 필터링해서 들고 올수 있으니 처리해야할 데이터도 그 만큼 적어져 성능면에서도 확실히 향상될것이다.

 

언제 쓰면 좋은가?

1. 쿼리를 수행할 대상 데이터가 많은 경우

2. 저장된 객체 중 일부 데이터만 실제 필요한 경우

3. 부분적인 데이터를 가져올 수 있어서  사용자 특화된 데이터만 얻고 싶은 경우

4. Spark나 Presto같은 tool을 사용해서 데이터 분석을 하기전 사전 필터링을 수행하고 싶은 경우

5. 에플리케이션 단에서 로드하고 처리할 데이터 양을 줄여서 성능 개선을 하고 싶은 경우

 

AWS의 모든 기능이 그렇듯, 공짜는 없으니 비용을 보면

(위 블로그는 미국 동부 기준으로 계산했으니 난 서울 기준으로, 5G를 스캔하고 2G를 )

S3-Select 로 스캔한 데이터 : 0.00225 USD/GB * 5 = 0.01125 USD

S3-Select 에서 반환한 데이트 : 0.0008 USD/GB * 2 = 0.0016 USD

합계 : 0.01285 USD

 

S3-Select를 사용하기 위한 준비

- Permission : s3:GetObject

- limitation: Expression 길이는 최대 256KB, 반환된 결과 Record 길이는 최대 1MB, 

- Data 구조: UTF-8 Encoding으로 JSON 또는 CSV

- 압축 포맷: GZIP, BZIP2

- S3-Select 함수: select_object_content()

 

 

select_object_content 에 들어갈 정보

Bucket 객체가 저장된 S3 Bucket
Key S3 file path
ExpressionType SQL 
Expression 요청할 SQL 문
InputSerialization
  • CSV | JSON | Parquet (필수) : 각 Type마다 속성 설정 가능 
    • CSV: RecordDelimiter, FieldDelimiter, FileHeaderInfo (예를 들어 USE: first line is header) ... 
  • CompressionType : NONE | GZIP | BZIP2 (선택)
CompressionType

CSV | JSON

각 Type마다 속성 설정 가능

  • CSV: QuoteFields, RecordDelimiter, FieldDelimiter, ....
  • JSON : RecordDelimiter (예를들어 \n)

 

Lambda 예제

1. Lambda 함수 생성시에 사용하는 Role에 데이터를 가져올 S3의 GetObject permission을 주는 policy를 추가해줘야한다.

2. Lambda python 코드

예제는 https://www.cloudberrylab.com/resources/blog/how-to-use-s3-select-feature-amazon/ 에 실제 S3에 올릴 파일과 결과가 잘 나와있어서 이것으로 이해

- S3 파일 : change-notice-police-department-incidents.csv 

import boto3


client = boto3.client('s3')

resp = client.select_object_content(
    Bucket='cloudberry-examples', # Put your own bucket name here.
    Key='s3-select/change-notice-police-department-incidents.csv', # Put your own key name here.
    Expression="SELECT * FROM s3object s WHERE s.Category = 'ASSAULT' and s.PdDistrict = 'MISSION' and s.\"Date\" BETWEEN '2004-12-31' AND '2005-01-01'",
    ExpressionType='SQL',
    InputSerialization={
        "CSV": {
            'FileHeaderInfo': 'USE',
        },
    },
    OutputSerialization={
        'JSON': {}
    },
)

for event in resp['Payload']:
    if 'Records' in event:
        print(event['Records']['Payload'].decode('utf-8'))
    elif 'Stats' in event:
        print(event['Stats'])

결과

$ python s3_select.py
{"IncidntNum":"050450449","Category":"ASSAULT","Descript":"THREATS AGAINST LIFE","DayOfWeek":"Friday","Date":"2004-12-31T00:00:00","Time":"08:00","PdDistrict":"MISSION","Resolution":"NONE","Address":"16TH ST / MISSION ST","X":"-122.419671780303","Y":"37.7650501214714","Location":"{'longitude': '-122.419671780303', 'human_address': '{\"address\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\"}', 'needs_recoding': False, 'latitude': '37.7650501214714'}","PdId":"5045044919057\r"}

{"IncidntNum":"050008684","Category":"ASSAULT","Descript":"THREATS AGAINST LIFE","DayOfWeek":"Friday","Date":"2004-12-31T00:00:00","Time":"18:00","PdDistrict":"MISSION","Resolution":"NONE","Address":"900 Block of ALABAMA ST","X":"-122.411278054259","Y":"37.7566750455245","Location":"{'longitude': '-122.411278054259', 'human_address': '{\"address\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\"}', 'needs_recoding': False, 'latitude': '37.7566750455245'}","PdId":"5000868419057\r"}

{"IncidntNum":"041476214","Category":"ASSAULT","Descript":"THREATS AGAINST LIFE","DayOfWeek":"Friday","Date":"2004-12-31T00:00:00","Time":"04:15","PdDistrict":"MISSION","Resolution":"NONE","Address":"800 Block of VALENCIA ST","X":"-122.421380986073","Y":"37.7589148884581","Location":"{'longitude': '-122.421380986073', 'human_address': '{\"address\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\"}', 'needs_recoding': False, 'latitude': '37.7589148884581'}","PdId":"4147621419057\r"}

{"IncidntNum":"041477278","Category":"ASSAULT","Descript":"BATTERY","DayOfWeek":"Friday","Date":"2004-12-31T00:00:00","Time":"11:43","PdDistrict":"MISSION","Resolution":"NONE","Address":"2700 Block of 22ND ST","X":"-122.409908449792","Y":"37.7560248782427","Location":"{'longitude': '-122.409908449792', 'human_address': '{\"address\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\"}', 'needs_recoding': False, 'latitude': '37.7560248782427'}","PdId":"4147727804134\r"}

{"IncidntNum":"050004143","Category":"ASSAULT","Descript":"INFLICT INJURY ON COHABITEE","DayOfWeek":"Friday","Date":"2004-12-31T00:00:00","Time":"19:00","PdDistrict":"MISSION","Resolution":"NONE","Address":"0 Block of LUCKY ST","X":"-122.413509818711","Y":"37.7517329980946","Location":"{'longitude': '-122.413509818711', 'human_address': '{\"address\":\"\",\"city\":\"\",\"state\":\"\",\"zip\":\"\"}', 'needs_recoding': False, 'latitude': '37.7517329980946'}","PdId":"5000414315040\r"}

{'Details': {'BytesScanned': 777628656, 'BytesProcessed': 777628656, 'BytesReturned': 2406}}

 

 

 

'자습' 카테고리의 다른 글

click을 이용한 python CLI  (0) 2019.09.08
pre-commit 활용하기  (0) 2019.08.31
파이썬 클린 코드 CH3. 좋은 코드의 일반적인 특징  (0) 2019.08.17
AWS Lambda@Edge  (0) 2019.08.10
Python Logging  (0) 2019.07.26