projection의 정렬과 분산 저장

Updated:

정렬(projection order by)

projection 생성시 order by 설정은 데이터 저장시 어떤 정렬로 데이터를 저장 할 것인지를 설정한다. 또한 옵티마이저가 최적에 projection을 선택 할 때 order by 설정이 어떻게 되어 있느냐에 따라서 projection을 선택한다.
아래 예제는 order by 설정을 다르게 해서 생성한 2개의 projection이 쿼리에 따라서 옵티마이저가 어느 projection을 선택하고 있는지 확인 할 수 있다. 또한 projection의 order by 설정은 오름차순(asc)으로 만 지정되므로 내림차순(desc)으로 조회하면 옵티마이저는 내림차순으로 정렬하는 단계가 추가 된다.
get_projection_sort_order()함수 또는 projection_columns테이블에 sort_position컬럼을 통해 projection의 order by 설정을 확인 할 수 있다.

dbadmin=> drop table if exists t4 cascade;
DROP TABLE
dbadmin=> create table t4(col1 int, col2 int);
CREATE TABLE

--segment key 지정 col1, order by 지정은 col1
dbadmin=> create projection t4_tune1 as select t4.col1, t4.col2 from t4 order by t4.col1 segmented by hash(t4.col1) all nodes;
CREATE PROJECTION

--segment key 지정 col1, order by 지정은 col1
dbadmin=> create projection t4_tune2 as select t4.col1, t4.col2 from t4 order by t4.col2 segmented by hash(t4.col1) all nodes;
CREATE PROJECTION

--데이터 insert
dbadmin=> insert into t4 values(100, 33);
dbadmin=> insert into t4 values(200, 22);
dbadmin=> insert into t4 values(600, 11);
dbadmin=> commit;
COMMIT

--projection확인 
dbadmin=> select anchor_table_name, projection_name from projections where anchor_table_name = 't4' order by 1, 2;
anchor_table_name | projection_name
-------------------+-----------------
t4                | t4_tune1_b0
t4                | t4_tune1_b1
t4                | t4_tune2_b0
t4                | t4_tune2_b1
(4 rows)

--col1로 정렬해서 t4 테이블 조회시 t4_tune1 projection사용
--t4 테이블 조회를 col1를 오룸차순으로 조회시 t4_tune1 projection사용
dbadmin=> explain select * from t4 order by col1 ;
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
explain select * from t4 order by col1 ;
Access Path:
+-STORAGE ACCESS for t4 [Cost: 403, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
|  Projection: public.t4_tune1_b0
|  Materialize: t4.col1, t4.col2
|  Execute on: All Nodes

--t4 테이블 조회를 col1를 내림차순으로 조회시 t4_tune1 projection 사용하고, 내림차순으로 다시 정렬하는 처리가 추가됨.
dbadmin=> explain select * from t4 order by col1 desc;
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
explain select * from t4 order by col1 desc;
Access Path:
+-SORT [Cost: 423, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
|  Order: t4.col1 DESC
|  Execute on: All Nodes
| +---> STORAGE ACCESS for t4 [Cost: 403, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| |      Projection: public.t4_tune1_b0
| |      Materialize: t4.col1, t4.col2
| |      Execute on: All Nodes

--t4 테이블 조회를 col2를 오룸차순으로 조회시 t4_tune2 projection사용
dbadmin=> explain select * from t4 order by col2;
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
explain select * from t4 order by col2;
Access Path:
+-STORAGE ACCESS for t4 [Cost: 403, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
|  Projection: public.t4_tune2_b0
|  Materialize: t4.col2, t4.col1
|  Execute on: All Nodes

복제(Replication - unsegmented projection)

projection의 unsegmented 설정은 모든 노드들이 동일한 데이터 복사본을 가지고 있다는 것이다. 각 노드에 데이터를 저장하므로 join이 로컬에서 일어나 조인 성능을 높일 수 있다. dimension 테이블과 같은 작은 테이블에 쓰이며, 10만건 이하의 테이블은 unsegmented projection으로 사용하면 조인시 유용하다.

버티카 unsegmented projection

아래 예제는 unsegmented projection과 segmented projection을 비교한다. unsegmented projection은 각 노드별로 생성되지만, segmented projection은 노드별 생성이 아닌 buddy projection을 가지고 생성이 된다.(k-safety 1적용시)

dbadmin=> drop table if exists t8, t9;
DROP TABLE

--unsegmented projection 생성
dbadmin=> create table t8 (col1 int , col2 int) unsegmented all nodes;
CREATE TABLE

--segmentd projection 생성
dbadmin=> create table t9(col1 int, col2 int) order by col1 segmented by hash(col1) all nodes;
CREATE TABLE

--projection테이블에서 unsegmented projection와 segmented projection 비교
dbadmin=> select anchor_table_name, projection_name, node_name, is_segmented, segment_expression from projections where anchor_table_name in ('t8', 't9') order by 1,2,3;
anchor_table_name | projection_name |     node_name      | is_segmented | segment_expression
-------------------+-----------------+--------------------+--------------+--------------------
t8                | t8_super        | v_test_db_node0001 | f            |
t8                | t8_super        | v_test_db_node0002 | f            |
t8                | t8_super        | v_test_db_node0003 | f            |
t9                | t9_b0           |                    | t            | hash(t9.col1)
t9                | t9_b1           |                    | t            | hash(t9.col1)
(5 rows)

분산(Segmentation - segmented projection)

projection의 segmented key설정은 데이터를 분산하는 key를 설정하는 것이다. 동일한 segmented key를 가지고 있다는 것은 데이터가 동일한 노드에 존재하여 테이블간 조인시 local join으로 처리 할 수 있다는 의미이다. 쿼리 성능을 높이기 위해서는 조인 키를 기준으로 동일하게 segmentation되거나 테이블이 복제(replication)되어 있어야 한다. 조인 키 기준으로 동일한 노드에 데이터가 분산되어 있지 않으면, 옵티마이저는 쿼리 실행시 데이터를 재배포(redistribution)해야 한다. 이렇게 되면 과도한 네트워크 트래픽의 원인과 과도한 메모리 사용을 유발 할 수 있다. 재배포(redistribution)을 확인하기 위해서는 쿼리 실행 계획에서 brodcast, resegmentation이 있는지 확인 하면 된다.

버티카 segmented projection

아래 예제는 동일한 segmented key를 가지는 테이블간 join과 동일하지 않는 segmented key를 가지 테이블간 join의 각각의 실행계획을 확인 할 수 있다.

dbadmin=> drop table if exists t5, t6, t7;
DROP TABLE

--col1을 segment key로 설정한 테이블
dbadmin=> create table t5(col1 int, col2 int) order by col1 segmented by hash(col1) all nodes;
CREATE TABLE
dbadmin=> insert into t5 values(100, 1);
dbadmin=> insert into t5 values(200, 2);
dbadmin=> insert into t5 values(600, 3);

--col1을 segment key로 설정한 테이블
dbadmin=> create table t6(col1 int, col3 int) order by col1 segmented by hash(col1) all nodes;
CREATE TABLE
dbadmin=> insert into t6 values(100, 11);
dbadmin=> insert into t6 values(200, 22);
dbadmin=> insert into t6 values(600, 33);

--col4을 segment key로 설정한 테이블
dbadmin=> create table t7(col1 int, col4 int) order by col1 segmented by hash(col4) all nodes;
CREATE TABLE
dbadmin=> insert into t7 values(100, 111);
dbadmin=> insert into t7 values(200, 222);
dbadmin=> insert into t7 values(600, 333);
dbadmin=> commit;
COMMIT

--동일한 segment key를 가지는 테이블간 join
dbadmin=> explain
dbadmin-> select t5.col1, t5.col2, t6.col1, t6.col3
dbadmin-> from t5
dbadmin-> inner join t6 on (t5.col1=t6.col1)
dbadmin-> ;
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
explain
select t5.col1, t5.col2, t6.col1, t6.col3
from t5
inner join t6 on (t5.col1=t6.col1)
;

Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 811, Rows: 10K (NO STATISTICS)] (PATH ID: 1)
|  Join Cond: (t5.col1 = t6.col1)
|  Materialize at Output: t5.col2
|  Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for t5 [Cost: 202, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| |      Projection: public.t5_b0
| |      Materialize: t5.col1
| |      Execute on: All Nodes
| |      Runtime Filter: (SIP1(MergeJoin): t5.col1)
| +-- Inner -> STORAGE ACCESS for t6 [Cost: 403, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| |      Projection: public.t6_b0
| |      Materialize: t6.col1, t6.col3
| |      Execute on: All Nodes


--동일하지 않은 segment key를 가지는 테이블간 join
dbadmin=> explain
dbadmin-> select t5.col1, t5.col2, t7.col1, t7.col4
dbadmin-> from t5
dbadmin-> inner join t7 on (t5.col1=t7.col1)
dbadmin-> ;
------------------------------
QUERY PLAN DESCRIPTION:
------------------------------
explain
select t5.col1, t5.col2, t7.col1, t7.col4
from t5
inner join t7 on (t5.col1=t7.col1)
;

Access Path:
+-JOIN MERGEJOIN(inputs presorted) [Cost: 849, Rows: 10K (NO STATISTICS)] (PATH ID: 1) Inner (RESEGMENT)
|  Join Cond: (t5.col1 = t7.col1)
|  Materialize at Output: t5.col2
|  Execute on: All Nodes
| +-- Outer -> STORAGE ACCESS for t5 [Cost: 202, Rows: 10K (NO STATISTICS)] (PATH ID: 2)
| |      Projection: public.t5_b0
| |      Materialize: t5.col1
| |      Execute on: All Nodes
| |      Runtime Filter: (SIP1(MergeJoin): t5.col1)
| +-- Inner -> STORAGE ACCESS for t7 [Cost: 403, Rows: 10K (NO STATISTICS)] (PATH ID: 3)
| |      Projection: public.t7_b0
| |      Materialize: t7.col1, t7.col4
| |      Execute on: All Nodes

 
auto projection의 segment key는 아래와 같은 기준으로 생성된다.

  • pk가 존재하는 경우 pk를 segmented key로 설정
  • long data type을 제외한 설정 파라미터 MaxAutoSegColumns에 지정된 갯수 만큼 자동으로 설정된다.
    • vertica 9.1까지 auto projection의 segment key는 long data type을 제외한 32개 column을 default로 segmented key로 설정.
    • vertica 9.2부터 auto projection의 segment key는 long data type을 제외한 8개 column을 default로 segmented key로 설정.