분산 키를 randomly 로 설정했을 때 (랜덤 분산)
특징:
- 데이터를 랜덤하게 분산시키는 방식이다.
- 특정 열을 기준으로 분산하지 않으므로, 모든 세그먼트에 데이터가 고르게 분산될 가능성이 높다.
- 해시 분산보다 더 균등한 데이터 분산을 기대할 수 있다.
적용 상황:
- 데이터 균등 분산: 데이터의 값이 불균등하거나, 특정 컬럼에 편중된 값이 있는 경우 randomly 분산은 세그먼트 간에 데이터와 부하를 고르게 나누는 데 유리하다.
- 쿼리 패턴이 랜덤한 경우: 쿼리가 특정 컬럼을 자주 필터링하지 않거나, 특정 컬럼을 기준으로 조인이 자주 일어나지 않는 경우에 적합하다.
- 분산 키를 지정하기 어려운 경우: 테이블에 명확하게 분산의 기준이 되는 컬럼이 없거나, 데이터의 자연스러운 분포를 예측하기 어려운 경우에 유용하다.
- 쓰기 성능 최적화: 데이터가 고르게 분산되므로, 각 세그먼트에 골고루 쓰기 작업이 분배되어 쓰기 성능이 향상될 수 있다.
단점:
- 특정한 키로 조인할 때 모든 세그먼트에서 데이터를 조회해야 할 수 있으므로, 조인 성능이 떨어질 수 있다.
- 쿼리 성능이 데이터의 분포나 액세스 패턴에 따라 일정하지 않을 수 있다.
CREATE TABLE employee_random (
emp_id INT,
emp_name TEXT,
department TEXT,
salary NUMERIC
) DISTRIBUTED RANDOMLY;
분산 키를 특정 컬럼으로 설정했을 때 (해시 분산)
특징:
- 특정 컬럼의 값에 따라 데이터를 해시 함수로 분산시킨다.
- 해시 값에 기반하여 데이터를 균등하게 분산하려고 시도하지만, 컬럼 값의 분포에 따라 불균형이 생길 수 있다.
- 해당 컬럼을 기준으로 자주 조회하거나 조인할 경우 성능이 최적화된다.
적용 상황:
- 자주 조인하거나 필터링하는 컬럼이 있는 경우: 조인이 자주 일어나거나, 특정 컬럼을 기준으로 필터링이 자주 이루어질 때 해당 컬럼을 분산 키로 설정하면 성능이 크게 향상될 수 있다. 특히, 동일한 분산 키로 여러 테이블을 설정하면 같은 세그먼트에서 데이터를 조인하게 되어 네트워크 오버헤드를 줄일 수 있다.
- 데이터 분포가 고르지 않더라도 특정 컬럼이 자주 사용될 때: 예를 들어, department가 반복적인 값을 가진다고 하더라도, department를 기준으로 자주 필터링하는 경우 성능을 향상시킬 수 있다.
- 특정한 조회 패턴을 최적화할 때: 예를 들어, emp_id를 기준으로 자주 조회하고 emp_id가 고유한 값이라면, 이를 분산 키로 선택하여 조회 성능을 극대화할 수 있다.
단점:
- 분산 키가 균등하지 않으면 데이터가 특정 세그먼트에 몰려 성능 저하를 일으킬 수 있다. 예를 들어, 분산 키가 고유하지 않고 자주 중복된다면, 일부 세그먼트에 많은 데이터가 집중된다.
- 해시 분산이 데이터를 특정 키에 집중시키지 않도록 키 선택에 주의해야 한다.
CREATE TABLE employee_by_emp_id (
emp_id INT,
emp_name TEXT,
department TEXT,
salary NUMERIC
) DISTRIBUTED BY (emp_id);
만약 데이터가 특정 컬럼에 몰려 있는 상황에서 join 이 자주 일어나는 컬럼일 경우?
Greenplum과 같은 분산형 데이터베이스에서는 조인 작업이 성능에 매우 중요한 영향을 미친다. 조인이 발생할 때, 두 테이블이 서로 다른 세그먼트에 분산되어 있으면 각 세그먼트 간에 데이터를 교환해야 하기 때문에 네트워크 오버헤드가 발생하게 된다.
조인에 자주 사용되는 컬럼을 분산 키로 설정하면 같은 세그먼트에 조인되는 데이터가 저장될 확률이 높아지기 때문에 네트워크 전송이 최소화된다. 즉, 조인이 분산된 각 세그먼트 내부에서만 이루어지게 되어 성능이 향상된다.
관련 TEST
테이블 employee (분산키 : emp_id) 또는 employee_by_department (분산키 : department) 를 다른 테이블 department_info 과 join 을 시켰다.
테이블 employee 는 적절하게 분산되어 있는 상태고,
테이블 employee_by_department 는 한 세그먼트에 데이터가 몰려있는 상태다.
explain SELECT e.emp_id, e.emp_name, e.department, d.manager, d.location, e.salary
FROM test.employee e
--FROM test.employee_by_department e
JOIN test.department_info d ON e.department = d.department
WHERE e.salary > 60000 -- 특정 급여 이상인 직원 필터링
AND d.location IN ('New York', 'San Francisco', 'Chicago') -- 특정 도시에서 근무하는 부서 필터링
AND e.department IN ('Engineering', 'Sales') -- 특정 부서 필터링
ORDER BY e.salary DESC -- 급여 기준으로 내림차순 정렬
LIMIT 10; -- 상위 10개 결과만 출력
employee | employee_by_department |
Limit (cost=0.00..862.00 rows=1 width=44) -> Gather Motion 6:1 (slice1; segments: 6) (cost=0.00..862.00 rows=1 width=44) Merge Key: e.salary -> Sort (cost=0.00..862.00 rows=1 width=44) Sort Key: e.salary DESC -> Hash Join (cost=0.00..862.00 rows=1 width=44) Hash Cond: (e.department = d.department) -> Redistribute Motion 6:6 (slice2; segments: 6) (cost=0.00..431.00 rows=1 width=28) Hash Key: e.department -> Seq Scan on employee e (cost=0.00..431.00 rows=1 width=28) Filter: ((salary > '60000'::numeric) AND (department = ANY ('{Engineering,Sales}'::text[])) AND (department = ANY ('{Engineering,Sales}'::text[]))) -> Hash (cost=431.00..431.00 rows=1 width=24) -> Seq Scan on department_info d (cost=0.00..431.00 rows=1 width=24) Filter: ((location = ANY ('{"New York","San Francisco",Chicago}'::text[])) AND (department = ANY ('{Engineering,Sales}'::text[]))) Optimizer: GPORCA |
Limit (cost=0.00..862.00 rows=1 width=44) -> Gather Motion 6:1 (slice1; segments: 6) (cost=0.00..862.00 rows=1 width=44) Merge Key: e.salary -> Sort (cost=0.00..862.00 rows=1 width=44) Sort Key: e.salary DESC -> Hash Join (cost=0.00..862.00 rows=1 width=44) Hash Cond: (e.department = d.department) -> Seq Scan on employee_by_department e (cost=0.00..431.00 rows=1 width=28) Filter: ((salary > '60000'::numeric) AND (department = ANY ('{Engineering,Sales}'::text[])) AND (department = ANY ('{Engineering,Sales}'::text[]))) -> Hash (cost=431.00..431.00 rows=1 width=24) -> Seq Scan on department_info d (cost=0.00..431.00 rows=1 width=24) Filter: ((location = ANY ('{"New York","San Francisco",Chicago}'::text[])) AND (department = ANY ('{Engineering,Sales}'::text[]))) Optimizer: GPORCA |
데이터 양이 많지 않으므로 차이는 없지만 join 하는 컬럼이 분산키가 아닐 경우 motion 이 발생한 것을 확인할 수 있다.
Redistribute Motion 6:6
department_info 테이블의 데이터가 하나의 세그먼트에 몰려있고, employee 테이블의 데이터는 6개의 세그먼트에 고르게 분산되어 있는 상태다.
이럴 경우 Greenplum은 조인을 최적화하기 위해 department_info 테이블의 데이터를 6개의 세그먼트로 분배하지 않고, 오히려 employee 테이블의 데이터를 department_info 테이블이 몰려있는 세그먼트로 전송할 가능성이 높다. 이는 효율성을 고려한 방식이다. 즉, 작은 테이블(department_info)에 맞춰 큰 테이블(employee)의 데이터를 조정하는 방식이다.
=> 큰 테이블을 재분배하는 것이 효율적이다.
'DB > PostgreSQL' 카테고리의 다른 글
GPDB - Query Plan 예시 (0) | 2024.10.01 |
---|---|
GPDB - motion 정의 (0) | 2024.09.12 |
PostgreSQL 로그인 권한 - 연결 실패 (0) | 2024.08.31 |
쿼리 튜닝용 파라미터 (0) | 2024.08.24 |
수동으로 데이터 재분배 (0) | 2024.08.06 |