Amazon Web Services 한국 블로그

Amazon Redshift에서 빠르게 데이터 로딩하기

Amazon Redshift는 페타바이트급의 데이터를 빠르고 저렴하고 간단하게 분석할 수 있는 클라우드 기반 데이터 웨어하우스(DW)서비스입니다. 병렬 실행, 압축 스토리지, 암호화 등 다양한 매니지드 서비스를 제공합니다. Amazon Redshift의 데이터 활용 크기는 160GB의 클러스터로 부터 1,000/TB/Year의 페타바이트급 대량 데이터를 다루는데 적합합니다.

우선 DW에서 데이터 로딩의 특성에 대해 살펴보겠습니다. 일반적으로 DW는 하나 이상의 서로 다른 데이터 소스에서 수집된 데이터의 통합 저장소로 사용합니다. 현재 및 과거의 데이터를 저장한 뒤, 데이터 분석가들이 기업 전체의 분석 보고서를 작성하는데 주로 사용됩니다. 상세한 일일 판매 분석, 연간 및 분기별 비교 또는 동향에 이르기까지 다양합니다.

일반적으로 데이터 분석가는 가능한 최신 데이터를 사용하고 싶어합니다만, 데이터 크기 및 업무의 특성으로 기존의 OLTP(Online Transaction Processing)와는 다른 방식으로 진행됩니다. 예를 들어, 웹 로그 분석 DW는 일반적으로 야간 또는 하루에 몇 번 데이터를 DW로 로드합니다. 하지만, 매 시간, 매 분 단위로 지속적으로 데이터를 갱신하려는 요구 사항도 있습니다.

이 글에서는 Amazon Redshift에서 짧은 주기로 지속적인 데이터를 로딩할 때, 더 빠르게 할 수 있는 모범 사례를 소개하고자 합니다. 이는 주별 또는 일별 로딩과는 다르게 빠른 시간 내에(수분 단위 등) 최신 데이터를 Amazon Redshift에 반영하기 위한 방법입니다. Amazon Redshift는 단일 MERGE 또는 UPSERT를 지원하지 않지만, 스테이징 테이블을 이용한 UPDATE 와 INSERT 조합을 이용하여 기존 테이블에 새로운 데이터를 효율적으로 추가할 수 있습니다. 스테이징 테이블에 아주 짧은 시간내에 데이터를 로딩하기 위해서 이 글에서 소개하는 방법이 여러분에게 의미 있을 것으로 생각합니다.

1. 데이터 입력 파일의 균형 맞추기
우선 Amazon Redshift 클러스터의 모든 컴퓨팅 노드를 효율적으로 사용하여 데이터를 로딩하기 위해서 COPY 명령어를 사용합니다. COPY 명령어는 Amazon S3, Amazon EMR, Amazon DynamoDB 또는 원격 호스트로 부터의 데이터 로딩을 병렬적으로 처리하기 때문에 많은 양의 데이터를 INSERT 구문에 비해 효율적으로 처리할 수 있습니다.

Amazon Redshift 클러스터는 노드들의 집합체 입니다. 클러스터의 각 노드는 자신의 운영체제, 전용 메모리 및 전용 디스크 저장 장치를 가지고 있습니다. 그 중 하나의 노드는 컴퓨팅 노드로 데이터 및 질의 처리 작업(query processing tasks)의 분배를 관리하는 리더 노드입니다. 그리고 컴퓨팅 노드의 디스크 스토리지는 몇 개의 슬라이스로 나누어 집니다. 이 때 슬라이스 갯수는 컴퓨팅 노드의 프로세서 갯수와 동일합니다. 예를 들어, DS1.XL 컴퓨팅 노드는 2개의 슬라이스를 가지고 있으며, DS1.8XL 컴퓨팅 노드에는 32개의 슬라이스가 있습니다. 가능한 모든 노드의 모든 슬라이스를 통해 고르게 데이터를 분산 저장하고, 병렬적으로 질의를 처리합니다.

가장 빠른 성능으로 데이터를 로딩하기 위해서는, 클러스터의 모든 슬라이스(하나의 가상 CPU, 메모리 및 디스크)에 동일한 양의 작업을 수행하도록 해야 합니다. 이것은 각 슬라이스당 수행 시간을 짧게 해주고, 선형에 가까운 확장을 보장합니다. 이를 위해서, 입력 파일의 수가 슬라이스 갯수의 배수인지 확인해야 합니다.

이제 여러분이 Amazon S3에서 데이터를 로딩하는 경우, Amazon S3에 업로드 하기 전에 Amazon Redshift 클러스터의 모든 슬라이스 갯수의 배수(1 또는 그 이상의)로 입력 파일을 분할하는 것이 필요합니다. 즉, 4대의 DS1.XL 컴퓨팅 노드로 클러스터를 생성하셨다면, 전체 슬라이스 갯수는 8 입니다. 이때 입력 파일을 8 또는 16/24/32 개로 분할하여 Amazon S3에 업로드 한 후, 단일 COPY 명령어로 모든 입력파 일을 병렬적으로 로딩하도록 하면 동시에 슬라이스 갯수만큼 로딩 작업이 수행됩니다.

아래 예는 Linux의 split 명령어를 통해 원하는 갯수로 파일을 분할하는 간단한 예제 입니다.

$ ls -la
total 12020
drwxrwxr-x 2 ec2-user ec2-user     4096 Jul 22 14:43 .
drwx------ 4 ec2-user ec2-user     4096 Jul 22 14:41 ..
-rw-rw-r-- 1 ec2-user ec2-user 12299878 Jul 22 14:41 customer-fw.tbl

$ split -l `wc -l customer-fw.tbl | awk '{print $1/16}'` -d --verbose customer-fw.tbl "customer-fw.tbl-"
creating file ‘customer-fw.tbl-00’
creating file ‘customer-fw.tbl-01’
...
creating file ‘customer-fw.tbl-14’
creating file ‘customer-fw.tbl-15’
$ ls -la
total 24052
drwxrwxr-x 2 ec2-user ec2-user     4096 Jul 22 14:44 .
drwx------ 4 ec2-user ec2-user     4096 Jul 22 14:41 ..
-rw-rw-r-- 1 ec2-user ec2-user 12299878 Jul 22 14:41 customer-fw.tbl
-rw-rw-r-- 1 ec2-user ec2-user   768750 Jul 22 14:44 customer-fw.tbl-00
-rw-rw-r-- 1 ec2-user ec2-user   768750 Jul 22 14:44 customer-fw.tbl-01
...
-rw-rw-r-- 1 ec2-user ec2-user   768750 Jul 22 14:44 customer-fw.tbl-14
-rw-rw-r-- 1 ec2-user ec2-user   768628 Jul 22 14:44 customer-fw.tbl-15

이제 Amazon Redshift를 통해 COPY 명령을 수행해 보겠습니다.

copy customer 
from 's3://<your-bucket-name>/load/customer-fw.tbl'
credentials 'aws_access_key_id=<Your-Access-Key-ID>; aws_secret_access_key=<Your-Secret-Access-Key>'

이제 Amazon S3에서 데이터를 로드하는 자세한 방법은 개발자 문서을 참고하시기 바랍니다.

2. 칼럼(Column) 압축 인코딩 미리 정의하기
Amazon Redshift 는 데이터가 저장될 때 크기를 줄일 수 있는 칼럼 레벨 압축을 지원합니다. 압축은 저장 공간을 절약하는 장점과 함께 스토리지로 부터 읽어야 할 데이터의 크기를 줄이기 위해 디스크의 I/O 를 줄임으로써 쿼리의 성능을 개선할 수 있는 방법입니다. Amazon Redshift 는 기본적으로는 압축되지 않은 형식으로 데이터를 저장합니다. 여러분은 테이블을 생성할 때 수동으로 각 칼럼의 압축 방식 또는 인코딩을 지정할 수 있습니다.

COPY 명령의 가장 큰 특징은 테이블이 비어있을 때 자동으로 최적의 칼럼 인코딩을 적용한다는 것입니다. 그러나, 이 작업은 약간의 시간이 필요합니다. 왜냐하면 자동 압축 분석을 위해서는 각 슬라이스 당 적어도 100,000행의 데이터를 먼저 샘플링 해야 하기 때문입니다. 아래는 COPY 명령을 통해 수행되는 자동 압축 분석 단계입니다.

  • 입력 파일로 부터 초기 샘플링을 위한 행을 로딩 합니다. 샘플 데이터의 크기는 COMPROWS 파라미터 값을 통해 정의되는데, 특별한 지정이 없을 시 기본값은 100,000 입니다.
  • 각 열에 알맞은 압축 옵션(인코딩 방식)이 결정됩니다.
  • 초기 로딩된 샘플 행이 테이블에서 제거됩니다.
  • 결정한 압축 옵션으로 테이블이 새롭게 생성됩니다.
  • 전체 입력 파일을 새로운 인코딩 방식을 사용하여 로딩하고 압축합니다.

추후에 추가적인 데이터가 해당 테이블에 추가될 때는 기존에 결정된 인코딩 방식을 이용하여 압축됩니다.

아주 짧은 주기로 스테이징 테이블에 데이터를 추가해야 하는 상황에서는 사전에 인코딩 방식을 결정하여 테이블을 생성해 놓는다면 이런 자동 압축 분석의 단계를 생략할 수 있습니다. 이때 각 컬럼에 알맞는 정확한 인코딩 방식을 미리 결정해 놓아야 합니다.

이렇게 하려면 아래와 같이 테이블을 대표할 수 있는 데이터를 한 번 COPY 명령어를 통해서 로딩하고 난 후, pg_table_def에 기록된 각 컬럼의 인코딩 방식을 사용합니다. 그런 다음 테이블을 DROP 하고 앞에서 찾은 컬럼의 인코딩 방식을 포함하여 테이블을 생성합니다. 이후 COPY 명령어를 사용하여 데이터를 로딩 할 때, 자동 압축 분석 작업을 생략하도록 ‘COMPUPDATE OFF’를 추가합니다.

select "column", type, encoding from pg_table_def where tablename = 'nations';

아래는 각 컬럼에 맞게 접합하게 선택된 인코딩 방법입니다.

Column Type Ecoding
nationkey integer DELTA
name character(25) TEXT255
regionkey integer RUNLENGTH
comment character varying(152) LZO

그런 다음 테이블을 DROP 하고, 테이블 생성 DDL에 ENCODE 키워드를 추가하여 컬럼에 대한 인코딩 방식을 명시하고 테이블을 다시 생성합니다.

CREATE TABLE NATIONS (
NATIONKEY INT ENCODE DELTA,
NAME CHAR(25) ENCODE TEXT255,
REGIONKEY INT ENCODE RUNLENGTH,
COMMENT VARCHAR(152) ENCODE LZO
);

이후 다시 데이터를 Amazon S3로 부터 COPY합니다.

copy nation 
from 's3://<your-bucket-name>/load/nations.tbl'
credentials 'aws_access_key_id=<Your-Access-Key-ID>; aws_secret_access_key=<Your-Secret-Access-Key>'
COMPUPDATE OFF;

3. 통계 계산의 빈도 줄이기
Amazon Redshift의 COPY 명령은 최적화(Optimizer)를 위해 각 데이터 로딩에 대해서 테이블의 통계를 계산합니다. 추후 질의(Query)를 위해서는 굉장히 유용하지만, 이 또한 시간이 걸립니다. 그래서 열 인코딩으로, 정해진 데이터 세트에 대해서 통계를 계산한 후 COPY 명령이 수행되는 동안 재계산을 막는 것이 빠른 데이터 로딩을 위한 좋은 방법입니다.

이를 위해서 압축 인코딩을 사전 정의하는 방식과 동일하게, 테이블을 대표할 수 있는 데이터 세트를 로딩한 다음, ANALYZE 명령어를 이용하여 분석을 할 수 있습니다. 그러고 난 후 매번 수행되는 COPY 명령어의 옵션으로 ‘STATUPDATE OFF’를 설정하여 자동 분석 작업을 중지할 수 있습니다.

단, Query를 효율성을 위해 테이블에 대한 통계정보를 최신으로 유지하기 위해서 주기적으로 ANALYZE 명령을 실행할 필요가 있습니다. 질의 설계기(Query planner)는 최적화된 query plan을 선택하기 위해서 테이블의 통계에 대한 메타데이터 정보를 사용하기 때문입니다.

만약 시간이 경과함에 따라 로딩한 데이터의 특성이 크게 변한 경우, 통계 및 컬럼 인코딩은 테이블 구조에 맞게 재계산해야 합니다.

4. 정렬키(Sort Keys) 순서로 데이터 로딩
Amazon Redshift은 테이블을 생성할 때 단일 또는 복수개 컬럼을 정렬키(Sort Keys)로 지정할 수 있습니다. Redshift는 1MB의 디스크 블록에 컬럼 데이터를 저장합니다. 각 블록에 저장된 최소값 그리고 최대값을 메타데이터로 관리합니다.

만약 질의에 범위 제한적인 조건이 포함된 경우, 테이블을 스캔하는 과정에서 필요한 블록만을 읽어 들일 수 있게 최소값, 최대값을 활용합니다. 질의에 필요한 블록에 대해서만 디스크 I/O를 일으킴으로써 질의에 대한 성능을 개선할 수 있는 좋은 기능입니다. 위와 같은 장점을 극대화 하기 위해, 데이터를 정렬키(Sort Keys)의 순서에 맞춰 로딩한다면 추후 테이블에 대한 VACUUM 작업을 회피할 수 있습니다.

Amazon Redshift 는 클러스터로 로딩하는 데이터를 정렬합니다.날짜 기반의 컬럼을 정렬키(Sort Keys)로 지정하셨다면 시간 순서로 데이터를 로딩하는 것이 좋은 방법입니다. 예를 들면, 정렬키로 설정된 순서대로 오후 1시 ~ 2시 데이터를 먼저 로딩하고 이후에 2시~3시 데이터를 로딩하는 방식입니다.

이 방식을 참고하여 데이터를 로딩한다면, VACUUM 명령을 delete 또는 update 작업의 결과로 발생하는 빈 공간(free space)을 회수하는 용도로만 사용하면 됩니다.

정렬키(Sort Keys)에 대한 자세한 내용은 기술 문서를 참고하시기 바랍니다.

5. SSD 노드 유형 사용하기
Amazon Redshift는 두 개(세부적으로는 6개)의 상이한 노드 유형을 제공하고 있습니다. 첫번째 ds1/ds2 패밀리는 마그네틱 디스크를 사용합니다. ds1(ds2).xlarge 노드 유형은 각 노드당 2TB의 사용 가능한 스토리지를 제공하고, ds1(ds2).8xlarge 노드 유형은 각 노드당 16TB 의 사용 가능한 스토리지를 제공합니다.

앞에서 설명드린 것 처럼 짧은 주기로 데이터를 로딩해야 하는 환경을 위해서라면 I/O 지연시간(latency)을 획기적으로 감소시킬 수 있는 SSD 디스크를 사용하는 dc1 패밀리를 사용하는 것이 도움이 됩니다. 데이터 로드의 간격(interval) 내에 원하는 데이터에 대한 로딩을 완료하기 위해서는 빠른 I/O가 필요 합니다. dc1.large는 각 노드당 160GB, dc1.8xlarge는 각 노드 당 2.56TB의 스토리지를 제공합니다. 이처럼 dc1 노드 유형은 높은 컴퓨팅-스토리지 비율을 제공하여 COPY 명령어를 수행할 때 낮은 지연 시간(latency)을 제공합니다.

지금까지 짧은 시간 간격으로 Amazon Redshift 로 데이터를 로드할 때 사용할 수 있는 다섯 가지 방법에 대해 알려드렸습니다. 특정 고객사의 경우, Amazon Redshift에서 위와 같은 방법들을 이용하여 데이터의 로드 시간을 90% 이상 개선한 경험이 있습니다. 물론 이러한 방법들이 모든 경우에 해당되는 것은 아닙니다. 다만, 하루에 한번 또는 일주일에 한번씩 배치(Batch)로 데이터를 로딩할 경우에는 약간 다른 방법을 사용 해야할 가능성도 있습니다. 여러분의 업무 목적에 맞는 적합한 방법을 사용하는 것이 업무의 효율성을 위해서는 매우 중요합니다.

마지막으로 2013년 re:Invent 에서 소개되었던 “Getting Maximum Performance from Amazon Redshift”세션에서의 HasOffers 사례 발표인 Fast Data Loads 부분도 참고해 보시길 권합니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 양승도 솔루션즈 아키텍트께서 작성해주셨습니다.
sd-yang-photo