Amazon RedShift에 대한 400 Level Session
Architecture and concepts
Basic Concepts
Basic Architecture
RedShift Spectrum*
RedShift Managed Storage*
RedShift AQUA*
Basic Concepts
Redshift는 기본적으로 PostgreSQL 백앤드를 확장하였습니다.
하지만 OLTP*인 PostgreSQL과 다르게 OLAP* 시스템을 따르고 있습니다.
OLTP* : Online Transaction Processing
OLAP* : Online Analytical Processing
— KEY DIFFERENCES BETWEEN OLAP vs. OLTP
또한 VPC, IAM, S3 와 같은 리소스들과 통합하여 AWS RedShift가 완성되었습니다.
Basic Architecture
RedShift는 MPP*, Columnar Archiecture*를 준수합니다.
MPP* : Massively Parallel Processing
Columnar Architecture* : 행기반 아키텍처로 다른 RDBMS는 일반적으로 Row-based Architecture를 따르고 있습니다. 구조적으로 Columnar Architecture가 다차원 연산을 비롯한 MPP에 유리합니다.
RedShift는 크게 Clients/BI Tools*, Leader Node*, Compute Node*, S3*로 구분됩니다.
Clients/BI Tools* : Redshift에 적재된 데이터를 가공 및 시각화
Leader Node* : Clients/BI Tools*에게 요청을 받고 Compute Node에게 작업을 지시, 작업의 결과를 취합하여 요청에 응답
Compute Node* : 최소 3개 이상의 Compute Node가 지시받은 작업에 대한 처리 및 데이터 저장을 진행
S3* : Compute Node들에 대한 Load, Unload, Backup, Retore 작업을 진행
이 중에서도 Leader Node, Compute Node들을 묶어서 RedShift Cluster라고 부릅니다.
RedShift Spectrum*
Compute Node*와 S3* 사이에는 Redshift Spectrum*을 추가로 배치할 수 있습니다.
RedShift Spectrum* : 가공 데이터를 CSV, JSON, Parquet 등으로 가공하여 데이터레이크로 노출시키기 위해서 사용
Redshift Managed Storage*
때로는 RedShift의 각 Compute Node에 대해서 Amazon RedShift Managed Storage*를 사용할 수도 있습니다.
이를 사용하면 각 Compute Node의 개별 스토리지가 64 TB까지 자동으로 확장되면서 처리하도록 구성할 수 있습니다.
Amazon RedSfhit Managed Storage* :
https://aws.amazon.com/ko/redshift/features/ra3/
RedSfhit AQUA*
Amazon RedShift Managed Storage* 환경에서는 데이터가 분산되어 있습니다. 이 경우 AQUA*를 사용하면 분산된 스토리지에 가까운 위치에 있는 AQUQ Node가 초근거리에서 데이터 처리를 진행하여 최종적으로 10x이상의 성능 향상을 할 수 있습니다.
해당 계층에서는 Encryption, Decompression, Filtering, Aggregate 등을 진행할 수 있어서 Hardware Accelerating의 이점을 누릴 수 있습니다.
AQUA* : Advanced Query Accelerator로서 Custom AWS-designed processor
https://aws.amazon.com/ko/blogs/korea/new-aqua-advanced-query-accelerator-for-amazon-redshift/
Terminology and concepts: Node Types
Node Types
Columnar
Node Types
주의할 점은 RedSfhit는 Instance Type별로 Disk Type, Size, MEM, vCPU, Slices가 다르다는 점입니다.
Columnar Architecture
가장 대중적인 RDBMS에서는 다양한 요구사항에 맞는 Business Logic을 처리하기 위해 Entire Row Data(행 전체 데이터)가 필요합니다. 따라서 Row-based Architecture(행 기반 아키텍처) 위에 설계되었으며, 자주 접근하는 열(Row)는 인덱싱 하는 것으로 발전하여 현대의 OLTP*가 되었습니다.
하지만 대규모 로그 분석의 경우 수십만~수십억 개의 Row의 일부 Column에 접근합니다. 따라서 Colum-based Architecture(열 기반 아키텍처)를 채택하면 전체적인 I/O를 향상시키고 쿼리 실행 결과가 개선됩니다.
예를 들어 아래와 같은 테이블이 있다고 가정해봅시다.
CREATE TABLE deep_dive (
aid INT --audience_id
,ioc CHAR(3) --location
,dt DATE --date
);
이 테이블에 대해서 다음과 같은 쿼리를 실행할 경우,
Row-based Arhictecture는 모든 Record를 탐색하지만
Column-based Architecture는 대상 Column( dt
) 만 탐색합니다.
SELECT min(dt) FROM deep_dive;
Encoding Type : AZ64
기존에 구축했었던 Kinesis Firehose, Glue Data Catalog, S3 based Log System에서는 Parquet 자료형을 사용했습니다.
RedShift는 작성일(2024-08-11) 기준으로 총 9개의 Compression Encoding Type을 지원하고 있습니다.
개인적으로 다음과 같이 사용를 구분할 수 있을 것 같습니다.
Encoding | Use Case | Data |
---|---|---|
AZ64 | 범용 로그 등에서 유용하게 사용 가능 | 문서 참고 |
Delta | 매우 많은 횟수 기록되며 RDMBS의 AUTO_INCREMENT 타입처럼 1씩 증분하는 Column)이 중심인 경우 | 문서 참고 |
Run Length | 카테고리처럼 유형이 적으며 동시에 연속성이 있는 경우 | (VAR) CHAR |
Text255/Text 32k | 카테고리처럼 유형이 적으나 연속성은 없는 경우 | (VAR) CHAR |
LZO, Mostly8, Mostly32k | 유형이 다양하나 최대 길이가 어느정도 정해지는 경우 | (VAR) CHAR |
ZSTD | 길이가 예측 불가능하게 길고 짧은 문자열 기반 | (VAR) CHAR |
Byte-Dictionary | 카디널리티(Cardinality)*가 낮은 경우 |
Raw Encoding : Sorted Key와 Boolean, Real, Double Precision 데이터 형식으로 정의되는 열에서 기본 인코딩.
AZ 64 Encoding : 단일 명령, 다중 데이터 명령(SIMD) 등을 사용하여 숫자, 날짜, 시간 데이터 유형을 크게 압축 가능. SMALLINT, INTERGER, BIGINT, DECIMAL, DATE, TIMESTAMP, TIMESTAMPZ 등에 특화
Byte-Dictionary : 모든 열 값 블록에 Dictionary를 생성, 카디널리티(Cardinality)가 낮은 값에서 효과적입니다.
Delta : 날짜/시간 열의 정보를 before → after의 차이로 기록
LZO, Mostly8, Mostly32 : 압축가능 범위는 압축, 그 외의 범위는 원시값으로 저장. 그 외의 범위 값이 많을 경우 Non-compression이 Compression보다 훨씬 적은 용량일 수 있습니다.
Run Length : 연속되는 단어와 횟수로 값을 압축. ‘blue’, ‘blue’, ‘blue’ 라면 ‘blue’ * 3 으로 압축하는 이치
Text255 / Text32k : 동일한 단어가 자주 반복되는 VARCHAR
ZSTD : 길고 짧은 문자열이 다양하게 저장되는 CHAR, VARCHAR 특화. Delta 및 Mostly에서 Non-compression 보다 Compression 용량이 더 커질 수 있는 사례에도, ZSTD는 디스크 사용량이 늘어날 가능성이 거의 없음
하지만, 실제 밴치마킹을 할 여러 리소스가 부족한 상황이라면 다음과 같은 가이드라인을 준수해도 좋을 것 같습니다.
AZ64 : INT, SMALLINT, BIGINT, TIMESTAMP, TIMESTAMPTZ, DATE, NUMERIC
LZO/ZSTD : VARCHAR, CHAR
Blocks
RedShift Cluster는 Block 단위로 데이터를 저장하며 이 Block은 1MB의 공간을 소모합니다. 한 번 정해진 Block은 절대로 변경할 수 없는 특징이 있습니다.
General : 300K Records in 1 Block
Best Usecase : 0000 K Records in Block
데이터 유형에 따라서 선택하는 Encoding Type이 Block 효율성에 큰 영향을 미칩니다.
Zone Maps
Zone Maps는 In-Memory Data Structure 중 하나입니다.
RedShift Cluster에서 Zone Maps는 기본적으로 각 Block의 최솟값과 최댓값을 가집니다.
RedShift Cluster가 Zone Maps를 자동으로 축적하며,
데이터 쿼리 시 Zone Maps를 이용하여 디스크에서 읽은 데이터를 필터링합니다.
Data Sorting
앞서 ZoneMap은 RedShift가 자동으로 축적 및 사용함을 알았습니다. RedShift에서 Unsorted vs Sorted Usecase를 살펴보면 ZoneMap이 어떤 것인지 명확하게 이해할 수 있습니다.
Unsorted Table
CREATE TABLE deep_dive ( aid INT --audience_id ,ioc CHAR(3) --location ,dt DATE --date );
Sorted Table
CREATE TABLE deep_dive ( aid INT --audience_id ,ioc CHAR(3) --location ,dt DATE --date ) SORTKEY (dt, ioc);
위의 2가지 테이블이 존재하고 아래의 쿼리를 실행해보겠습니다.
SELECT COUNT(*) FROM deep_dive WHERE dt = '08-09-2017';
Unsorted Table의 Zone Map에서는 분포가 흩어져 있으므로 다양한 Map에 탐색을 실행해야 합니다. 하지만 Sorted Table의 Zone Map에서는 특정한 Map만 탐색을 진행합니다.
복수의 Soreted Key 사용 시, 하이 카디널리티 우선할 것
이러한 Sorted Key의 경우, 카디널리티가 높은 값이 우선되는 것이 Best Practices입니다.
일반적으로 Timestamp가 카디널리티가 높지만,
특수한 경우에서는 다른 Column의 카디널리티가 높을 수 있습니다. 이런 경우 다음과 같이 Sorted Key가 구성될 수도 있습니다.
CREATE TABLE deep_dive (
aid INT --audience_id
,ioc CHAR(3) --location
,dt DATE --date
) SORTKEY (ioc, aid, dt);
audience_id >> location >> dt 순으로 카디널리티가 높은 경우
최대 1~3개 사이의 Sorted Key만 사용할 것
일반적으로 Sorted key의 종류 수가 늘어날 수록 Sorted Benefit보다 Sorted Cost이 커집니다. 따라서 항상 1~3개 사이의 Sorted Key를 유지하는 것이 좋습니다.
Materialize Columns(16 mintues)
일반적으로 OLTP 환경의 RDB에서는 테이블드를 심하게 정규화합니다.
하지만 OLAP 환경의 RedShift에서 쿼리 실행 속도 향상을 위해서 Fact Table과 ZoneMap을 활용할 수 있습니다. 자주 필터링되고 변경되지 않는 값은 Fact Table에서 구체화되어야 합니다.
Time Dimension Table은 Fact Table에서 범위 제한 조회를 허용하지 않습니다.
따라서 해당 Fact Table에서 Time 값을 구체화하면 상당한 성능 향상을 기대할 수 있습니다. 이 기능은 Resource & Process 그룹에 동일한 Namespace를 사용하지만, 이러한 Namespace는 서로 다른 Resource를 참조하는 방식으로 작동합니다.
즉, RedShift에서는 정규화된 값과 테이블을 이용하는 경우보다 정규화 칼럼을 만들어서 구체화한 단일 테이블을 만드는 것이 훨씬 효율적입니다.
Bad Case : 정규화된 값과 테이블을 그대로 사용
Good Case : 정규화(=구체화)된 값을 만들어서 단일 테이블을 사용
느린 쿼리
SELECT COUNT(*) FROM fact_table JOIN dimension_table USING (time_id) WHERE dimension_table.timestamp > '2018-11-29';
빠른 쿼리 (More Faster)
SELECT COUNT(*) FROM fact_table WHERE fact_table.timestamp >= '2018-11-29';
아래와 같은 경우도 존재합니다.
느린 쿼리
SELECT COUNT(*) FROM dd WHERE EXTRACT(EPOCH FROM timestamp) BETWEEN 1541120959 AND 1543520959;
빠른 쿼리 (More Faster)
SELECT COUNT(*) FROM dd WHERE sorted_epoch BETWEEN 1541123959 AND 154320959;
Slices
RedShift Cluster는 기본적으로 복수의 Compute Node로 구성됩니다.
이러한 복수의 Compute Node가 병렬성을 얻는 방법이 Slices입니다.
병렬성을 얻는 가장 쉬운 방법은 Virtual Compute Node입니다.
(Node 크기에 따라) 모든 Compute Node는 2개 또는 16개로 나누어집니다.
이런 Virtual Computing Tech.를 Slices라고 부릅니다.
즉, 모든 테이블의 Column은 여러 개의 Slices로 분산되어 저장됩니다.
Data Distribution
RedShift에서 데이터 분산을 시키는 방법은 KEY*, ALL*, EVEN*, AUTO*가 존재합니다.
KEY* : HashValue를 이용하며, 같은 Value는 같은 Slice로 위치한다.
ALL* : 모든 Table은 모든 Node의 첫 번째 Slice에 데이터를 삽입한다.
EVEN* : Round Robin 방식으로 데이터를 삽입
AUTO* : ALL*과 EVEN* 방식의 조합
실제로 ALL*, EVEN*에 대해서 조금 더 살펴보면 다음의 순서로 데이터가 삽입됩니다.
ALL* : Node1/Slice1 && Node2/Slice1 → Node1/Slice1 && Node2/Slice1
EVEN* : Node1/Slice1 → Node1/Slice2 → Node2/Slice1 → Node2/Slice2
(&&는 동시에를 표시하는 단어로서 사용하였습니다.)
각각의 Data Distribution 방식이 적합한 사용 사례가 다르므로,
데이터 테이블의 특징에 맞는 Data Distribution 방식을 선택하는 것이 좋습니다.
Data storage, ingestion, and ELT
데이터 처리와 관리 개념인 Data Storage*, Ingeation*, ELT*에 대해서 배웁니다.
Data Storage* : 데이터 저장소 (데이터베이스, 데이터웨어하우스, 스토리지 등)
Ingestion* : 데이터를 다양한 소스(원본)에서 수집하고 이를 시스템에 로드
ELT* : Extract, Load, Transform