안녕하세요. 저는 번개장터 백앤드 엔지니어 이예진이라고 합니다. 이번에 번개장터의 클릭 로그 시스템을 개선하면서 사용한 AWS Athena를 소개하고 사용하는 방법을 공유하고자 글을 쓰게 되었습니다.
기존의 클릭 로깅 구조는 fluentd에서 로그를 수집하고 DB에 적재해서 필요할 때 DB에 접근하는 구조였습니다. 시간이 지나면서 DB에 로그들이 많이 쌓이게 되었고 이 때문에 부하가 가는 문제점이 생겨 로그 시스템을 개선할 필요가 있었습니다. 저희는 로그를 DB 외에도 다양한 곳에서 적재를 하는데 그 중 AWS S3에도 저장을 하고 있었기 때문에 S3를 사용해 DB를 대신하는 작업을 진행하게 되었습니다.
DB에서 가져오던 로그를 동일하게 S3에서 가져오는 것은 제약이 많았습니다. 로그를 가져올 때 조건을 거는 부분과 많은 양의 압축된 로그 파일을 로드하고 압축을 푸는데 시간이 너무 많이 소요되는 부분 등이 문제가 되었습니다.
이러한 문제들은 AWS Athena를 통해 해결할 수 있고 로그를 효과적으로 가져올 수 있다는 점에서 사용하게 되었습니다.
Amazon Athena은(는) Amazon Simple Storage Service(Amazon S3)에서 표준 SQL을 사용하여 데이터를 쉽게 바로 분석할 수 있는 대화형 쿼리 서비스입니다
아테나에 데이터가 저장되어있는 S3를 설정해주고 테이블 생성 후 쿼리를 실행하기만 하면 데이터를 가져올 수 있습니다. 파일을 로드하고 압축을 풀지 않아도 되고 SQL문을 통해 제약 조건 걸어 원하는 데이터만 가져올 수 있게 되는 거죠. 그것도 빠르고 쉽게!
이 외에도 아테나의 매력은 더 있습니다.
서버리스 서비스
아테나는 서버리스 서비스이기 때문에 인프라를 관리할 필요가 없습니다.
쿼리 당 비용 지불
아테나에서는 실행한 쿼리에 대한 비용을 지불하면 됩니다. 실행한 쿼리가 스캔한 데이터의 용량만큼 비용이 청구됩니다. 추가로 데이터 압축과 파티셔닝을 하게 되면 스캔하는 데이터의 양을 제한해 비용을 절감할 수 있으며 아테나를 사용하기 위한 테이블 생성(CREATE)이나 파티셔닝을 위한 테이블 수정(ALTER)과 같은 DDL문과 실패한 쿼리문에 대한 비용은 청구되지 않습니다.
지금까지 아테나가 어떤 서비스인지 간략히 알아보았습니다. 이제 직접 아테나를 사용해보면서 좀 더 자세히 알아보겠습니다.
아테나를 잘 사용하기 위한 성능 향상 팁들을 알아보고 직접 적용해서 사용해봅시다.
데이터 파티셔닝이란 S3에 쌓인 많은 로그를 조회할 때 성능을 향상시키고 비용을 절감하는 방법입니다. 파티셔닝을 하지 않으면 아테나 query는 모든 데이터를 스캔하지만 파티셔닝을 하면 쿼리 당 스캔되는 데이터의 양을 줄일 수 있습니다. 그래서 아테나를 사용할 때 파티셔닝은 필수로 하는 것이 좋습니다.
파티셔닝은 보통 날짜 기준으로 하게 됩니다. 날짜를 기준으로 query가 스캔하는 범위를 제한하는거죠. 2019년 3월 7일의 로그 데이터를 수집할 때 10년치 로그가 쌓여있는 한개의 폴더에서 찾을 경우 10년치 로그를 모두 스캔해야하지만 로그가 각 년,월,일로 분할되어 있는 폴더에서 찾을 경우엔 bucket/2019/3/7
폴더에 있는 로그만 스캔하면 됩니다.
파티셔닝은 자동으로 맵핑하는 방법과 수동으로 맵핑하는 방법이 있습니다. 만약 S3에 이미 로그가 쌓여져 있으면 둘 중에 자신의 S3 구조에 맞는 방법을 사용하면 되고, 아직 로그가 쌓여지 않은 상태라면 자동 맵핑 구조에 맞게 데이터를 적재하면 더욱 편리할 것입니다.
자동 맵핑을 하기 위해서는 버킷에서 아래와 같은 구조로 파일이 위치해야합니다.
S3://your-bucket/pathToTable/<PARTITION_COLUMN_NAME>=<VALUE>/<PARTITION_COLUMN_NAME>=<VALUE>/
예를 들면
S3://my-bucket/log-data/year=2019/month=3/day=7
or
S3://my-bucket/log-data/dt=20190307/
이러한 파일 구조를 가지고 있어야합니다. 만약 내 버킷은 이런 구조가 아니다! 하면 수동 맵핑을 해주면 됩니다.
여기서 year, month, day, dt는 범위를 설정하기 위한 파티션 컬럼입니다. 쿼리가 가능하도록 구조만 똑같이 하면 되고 컬럼명과 형식은 달라도 됩니다. 이렇게 파일이 위치한 상태에서 테이블 생성 시 파티션 컬럼을 설정해줍니다.
CREATE TABLE mylogs (
...
)
PARTITIONED BY (year int, month int, day int)
테이블 생성 후 자동으로 파티션을 지정할 수 있도록 아래의 명령을 수행합니다.
MSCK REPAIR TABLE mylogs
이렇게 자동 맵핑 설정을 마쳤습니다. 자동 맵핑은 자동으로 파티션 컬럼을 맵핑해주기 때문에 추가로 파티셔닝을 해주지 않아도 됩니다.
만약 자동 맵핑을 할 수 없다면 수동 맵핑을 해줍니다.
s3://my_bucket/log-data/20180307/
위와 같이 파일 구조에 파티션 컬럼이 존재하지 않아 자동 맵핑을 할 수 없는 구조라면 ALTER 문을 통해 수동으로 파티셔닝을 해줍니다.
ALTER TABLE <tablename> ADD PARTITION (PARTITION_COLUMN_NAME = <VALUE>, PARTITION_COLUMN2_NAME = <VALUE>) LOCATION ‘s3://yourBucket/pathToTable/YYYY/MM/DD/
예를 들면
CREATE TABLE mylogs (
...
)
PARTITIONED BY (dt string)
먼저 테이블 생성, 파티션 컬럼을 지정해주고
ALTER TABLE mylogs ADD PARTITION (dt='20190307') location 's3://my_bucket/log-data/20180307/'
이렇게 일자별로 파티션을 등록해줍니다. 위 예제 구조는 일자별로 로그를 모으기 때문에 매일 새로운 폴더가 생기는데 그때마다 파티션을 등록해줘야 합니다.
파티셔닝을 해주었다면 현재까지 생성된 파티션을 아래 쿼리를 통해 확인할 수 있고, 데이터 조회 시 더욱 향상된 쿼리를 실행할 수 있습니다.
SHOW PARTITIONS <table_name>
SELECT * FROM mylogs WHERE dt = '20190307' LIMIT 100
아테나는 CSV, TSV, JSON, 사용자 지정 구분 기호 등의 다양한 데이터 형식을 지원합니다. 테이블 생성 시 아테나가 사용되는 형식과 데이터 구문 분석 방법을 알 수 있도록 SerDe
라이브러리를 지정합니다.
CREATE TABLE mylogs (
...
)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
위 예제는 SerDe에 JSON 데이터 형식을 지정한 것입니다. 아테나가 지원하는 데이터 형식과 라이브러리는 이 곳에 나와있습니다.
또한 아테나 사용시 데이터를 압축해서 사용하는 것을 추천합니다. 파일 용량이 작으면 네트워크 비용을 줄일 수 있어 쿼리 속도가 향상되고 스캔되는 데이터의 크기도 작아지기 때문에 비용도 절감할 수 있습니다. 지원 되는 압축 형식은 GZIP, SNAPPY 등이 있고 테이블 생성 시 압축 파일 형식을 지정해줘야하는데 형식을 생략하면 기본적으로 GZIP이 사용됩니다. 압축에 대한 더 자세한 내용은 이 곳에서 확인할 수 있습니다.
이 외에도 아테나는 PrestoDB를 사용하기 때문에 PrestoDB를 알아보고 쿼리를 최적화 하는 방법을 통해 성능을 향상시킬 수 있습니다.
아테나에 대해 좀 더 자세히 알아보았으니 이제 사용하는 방법을 알아봅시다. 예시를 위해 우리가 조회할 로그는 아래와 같은 JSON 형태이고 로그들은 gz 확장자 명의 GZIP 형식으로 압축되어있다고 가정합니다.
{"id": 000001, "type": "click", "status": 0, "created_at": "2019-02-26 00:00:00"}
아테나 콘솔창에서 데이터베이스를 생성합니다.
CREATE DATABASE mydatabase
데이터베이스를 생성했다면 테이블을 생성해야 합니다. 테이블 생성문은 SQL의 CREATE문과 거의 동일합니다. 테이블 스키마는 조회하려는 S3 데이터 구조에 맞게 작성해야 합니다. 밑의 간단한 테이블 생성문 예제를 하나씩 살펴보겠습니다.
CREATE EXTERNAL TABLE mylogs (
id int,
type string,
status int,
created_at timestamp,
) PARTITIONED BY (dt string)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
LOCATION 's3://my_bucket/log-data/'
CREATE EXTERNAL TABLE [table_name]
테이블 이름을 설정해줍니다. athena는 테이블 생성시 EXTERNAL
키워드 꼭 사용해야한다고 합니다.
cid int, type string, created_at timestamp
로그 데이터를 기준으로 컬럼을 설정해줍니다.
PARTITIONED BY (dt string)
dt(date)
날짜 컬럼 기준으로 데이터 파티셔닝을 설정해줍니다.
ROW FORMAT SERDE ‘org.apache.hive.hcatalog.data.JsonSerDe’
데이터 형식을 분석하기 위한 라이브러리 설정을 해줍니다. 예제 로그 데이터는 JSON 형태이기 때문에 json 직렬화 라이브러리 를 사용하였습니다.
LOCATION ‘s3://my_log/log/’
조회할 로그 데이터들이 있는 s3 주소를 설정합니다.
위에서 보았던 데이터 파티셔닝을 진행합니다. 자동맵핑과 수동맵핑 중 자신에게 맞는 파티셔닝 방법을 사용하면 됩니다.
이제 아테나를 사용하기 위한 준비는 끝났습니다! 이제 S3 데이터를 쿼리를 통해 조회할 수 있습니다.
SELECT id, type FROM mylogs WHERE dt = '20190307' AND status = 0 LIMIT 100
이번 글에서는 AWS Athena가 어떤 서비스인지와 사용하는 방법까지 알아보았습니다. 이 글을 통해 저와 같이 아테나를 사용하면서 레퍼런스의 부족함을 느낀 분들에게 도움이 되었으면 좋겠습니다. 부족한 글을 읽어주셔서 감사합니다. 잘못된 내용이 있을 경우 피드백 주시면 감사하겠습니다.
글쓴이 : 이예진 (Lowell)
이메일 : lowell.lee@bunjang.co.kr