Greenplum

Greenplum - DDL

n.han 2019. 12. 13. 12:12

- DDL

GP에선 CHAR 대신 TEXT나 VARCHAR를 써야 한다. INT / SMALLINT로 충분한 경우 BIGINT를 쓰지 않아야 한다. JOIN에 활용되는 컬럼들의 데이터타입은 같게 해야 한다. JOIN 되는 데이터타입이 다르면 형변환이 발생하는데 그때 INDEX, PARTITION 등이 타지 않을 수 있고 JOIN 자체가 안될 수 있다.

CREATEA TABLE: Storage model

Row oriented, Column oriented 방식으로 테이블을 저장할 수 있다. Row oriented는 Row 별로, Column은 Column 별로 파일을 저장한다. Row는 WHERE절에 Filtering하게 될 때 장점이 있지만 컬럼 별로 SELECT을 하게 되면 Column 방식에 비해 속도가 느릴 수 있다. Column은 Vice versa. 일반적으로 쿼리 실행 속도가 중요한 경우 Latency를 보장하기 위해서 Row oriented로 방식을 쓴다.
테이블은 Heap Storage / Append-Optimized Storage에 저장된다. CREATE TABLE시 WITH 절에 appendonly=true 옵션을 주면 Append-Optimized Storage에 저장된다. Heap Storage에는 Row Oriented만, Append-Optimized Storage에는 Row Oriented와 Column Oriented, Compression 등이 저장된다.
CREATE TABLE시 WITH절에 활용할 수 있는 옵션은 appendonly, orientation, compresstype 등이 있다. appendonly는 테이블이 DELETE, UPDATE가 거의 일어나지 않고 계속 Append되는 형태에 최적화되어 있다. orientation은 row, column이 있고, 설정된 방식으로 저장된다. compresstype으로 테이블의 압축유무와 타입을 지정할 수 있다.
병목이 발생하는 곳은 Disk IO / CPU / Network로 나뉠 수 있는데, 거의 대부분 Disk IO시 발생한다. 따라서 Disk IO를 줄일 필요성이 있는데, 이를 위해 압축을 활용할 수 있다. 테이블 압축을 하면 동시성이 좋아진다. Segment에서 데이터를 가지고 오는 양 자체가 줄어들기 때문이다. 압축된 데이터들은 어디서 압축이 해제 될까? 각 Segment에서 압축이 된다. 일단 compress를 하려면 appendonly를 해야 한다. appendonly를 하지 않는 기본적인 방식은 데이터를 저장할 때 어느정도 여백을 추가하는데, appendonly는 여백의 크기가 작다. 따라서 공간이 절약되고, 많은 데이터를 INSERT할 때 좋다. 하지만 모두 일반론적인 이야기이고, 뭐가 좋은지는 case by case로 분석해야 할 것 같다.

Distribution

GP DB은 MPP이기 때문에, 데이터들을 Segment로 나누는 것이 중요하다. 특정 컬럼을 분산 키로 잡아서 Hash해서 나누거나, 자동적으로 RANDOMLY로 하게 되면 알아서 잘 분배되도록 할 수 있다. 분배에 대한 정도는 Data Skew와 Processing Skew로 나뉜다. Data Skew는 데이터 자체를 고르게 나뉘었는지에, Processing Skew은 쿼리의 WHERE 때문에 특정 Segment만 열심히 일하게 되는 경우와 관련된 것이다.

Best Distribution for Join와 Join의 종류: Local Join, Broadcast motion, Redistribution

회원 테이블과 성별 테이블들이 있고, 이 테이블들이 수십개의 인스턴스에 분산되었다고 하자. 두 테이블들을 조인하는 경우, 두 테이블들의 분산키가 '회원번호'로 같으면 각 인스턴스 내에서 JOIN이 되면 된다. 이를 Local Join이라고 한다.
만약 성별 테이블이 '성별'로 분산 되어 있는 경우, 그리고 이 테이블의 크기가 작다면 데이터들을 Join되기 위해서 잠시 복사해주는 Broadcast motion을 하면 된다.
성별 테이블이 아니라, 회원들이 구매한 구매 이력 테이블을 생각해보자. 분산키가 다르게 설정되어 있는 회원 테이블과 구매 이력 테이블을 Join하게 되면, Local Join은 불가능하고 Broadcast motion하게 되면 인스턴스 간 수많은 데이터들이 오가게 되어 overhead가 심해지게 된다. 따라서 전체 데이터를 오가게 하지 않고, 필요한 데이터들만을 Join 가능하도록 Redistribution join이 발생한다.
Broadcast motion과 Redistribution join을 하는 통계 정보를 참조한다. 데이터 양이 적으면 Broadcast하고, 많으면 Redistribution한다. Broadcast join이 효과적이려면 Max 10만건이어야 한다. 따라서 통계 정보를 업데이트하는 것이 필요하다.

유용한 팁

  1. 쿼리 날리는 이력들이 로그로 남고, 이 로그들을 테이블화할 수 있다. 쿼리 수행 시간들도 모니터링할 수 있기 때문에 매우 유용해보인다.
  2. 추가 예정...

Data Types

GP DB에서 모든 object들은 Object-ID(OID(Object Identifier Type))가 있다. OID로 객체들을 구분할 수 있다. SELECT 23.4::TEXT;와 같이 형변환할 수 있다. hstore와 같은 key, value pair, JSON와 같은 Data Type도 제공한다.

Indexes

자주 쓰는 컬럼에 대하여 Index를 생성하면 SELECT가 빨라지지만, 데이터를 Load하는데 시간이 많이 걸린다. Index는 또 압축이 되지 않는다. 테이블 성능이 느려진 것 같으면, Index를 Rebuild해보는 것도 좋은 해결책이 될 수 있다.

분산키

분산도가 좋고, Local Join을 하기 위해 Join이 많은 컬럼을 대상으로 분산키를 잡아야 한다. 
select gp_segment_id, count(*) from 테이블이름 group by gp_segment_id order by 1;
명령어를 날리면, 해당 테이블의 data skewness를 확인할 수 있다. data skewness와 process skewness 간 서로 경합이 발생할 수 있을 것 같은데, 우선순위를 어떻게 생각해야 할까? case by case일 것 같다.
explain 쿼리; 혹은 explain analysis 실행된쿼리;를 입력하면, segment 별로 실행 속도를 확인할 수 있어서 processing skewness까지 확인할 수 있다.

Storage Optimization

Partitioning. 큰 테이블을 작은 논리적 부분으로 나눈다. 쿼리 성능이 좋아진다. GP DB에서는 Range, List Partitioning이 있다. 시간 컬럼이 있는 경우, 월별로 데이터들을 Range Partitioning할 수 있다. 그렇게 되면 월별 데이터를 SELECT할 때 특정 부분만 보게 된다. 일별로 데이터들을 List Partitioning할 수도 있다. 시간 컬럼 자체를 Index로 잡는 것과 무엇이 다를까? SELECT할 때 성능은 비슷할 것 같다. 하지만 INSERT와 같은 다른 DML들은 Index가 있는 경우 Indexing을 하는 비용이 드니, 더 나을 듯 하다.
Analyze. analyze 관련하여 gp_autostats_mode라는 옵션이 있다. gp_autostats_mode은 NONE, ON_NO_STATS, ON_CHANGE 모드가 있다. 보통 ON_NO_STATS로 해서 없으면 자동으로 돌려주도록 하고, 필요하다면 개발자가(DBA가) 알아서 돌리는게 낫다.

 

'Greenplum' 카테고리의 다른 글

Greenplum - Backup and Recovery  (0) 2019.12.13
Greenplum - Data manipulations  (0) 2019.12.13
Greenplum - Loading Data  (0) 2019.12.13
Greenplum - Fundamental Concepts  (1) 2019.12.10