파티셔닝은 한 표의 행을 정해진 기준으로 여러 조각에 나눠 저장하는 것입니다. 조각 하나를 파티션이라 하고, 질의는 나뉜 것을 모르는 채 표 하나처럼 씁니다.
반정규화에서 큰 표를 행으로 가르는 수평 분할을 배웠습니다. 그때는 표를 둘로 쪼개 이름이 다른 표 둘로 만들었습니다 — 질의도 어느 표를 읽을지 골라 써야 했습니다. 파티셔닝은 같은 일을 DBMS가 표 하나 안에서 해 줍니다 — 표 이름은 그대로 하나이고 질의는 고치지 않습니다. 행이 어느 파티션에 들어갈지는 기준 열의 값으로 정해지고, 그 규칙이 파티셔닝의 종류입니다. 아래 그림은 같은 표를 세 가지 규칙으로 나눠 봅니다.
1값으로 칸을 고르는 둘 — 범위와 목록
범위 파티셔닝은 기준 열의 값이 어느 구간에 드는지 보고 파티션을 고릅니다. 수강 표를 점수 70 미만·70 이상 90 미만·90 이상으로 나누면 점수 85인 행은 가운데 파티션에 들어갑니다. 날짜·금액·점수처럼 순서가 있는 값에 씁니다.
목록 파티셔닝은 기준 열의 값이 어느 목록에 적혀 있는지 보고 고릅니다. 학생 표를 학과코드 'CS'·'EE'로 나누는 것이 그렇습니다. 지역·부서·상태처럼 값이 정해진 몇 가지뿐일 때 씁니다.
두 종류 모두 어느 파티션도 받지 못하는 값이 들어오면 행이 들어가지 못합니다. 그래서 범위에는 마지막 구간을 MAXVALUE로 열어 두고, 목록에는 그 밖의 값을 받는 파티션을 DEFAULT로 둡니다.
2계산으로 칸을 고른다 — 해시와 조합
해시 파티셔닝은 기준 열의 값을 계산해 파티션을 고릅니다. 파티션 수만 정해 주면 어느 행이 어디로 갈지는 DBMS가 정합니다. 값의 종류가 아주 많고 어디에 몰릴지 모를 때, 행을 고르게 흩어 놓으려고 씁니다.
PARTITION BY HASH (학번) (
PARTITION P1,
PARTITION P2
)
구간도 목록도 적지 않는 것이 눈에 보이는 차이입니다. 그래서 '2025년 것만 떼어 내기'처럼 값을 알고 하는 관리는 해시로 할 수 없습니다.
조합 파티셔닝은 한 기준으로 나눈 파티션을 다른 기준으로 한 번 더 나눕니다. 안쪽 조각을 서브파티션이라 합니다. 범위로 나눈 파티션이 너무 커질 때 그 안을 해시로 고르게 흩는 쓰임이 많습니다.
PARTITION BY RANGE (점수)
SUBPARTITION BY HASH (학번)
SUBPARTITIONS 2
| 가르는 기준 | 범위 | 목록 | 해시 | 조합 |
|---|---|---|---|---|
| 파티션을 고르는 것 | 값이 든 구간 | 값이 적힌 목록 | 값을 계산한 결과 | 바깥 기준 다음 안쪽 기준 |
| 기준 열에 맞는 값 | 순서가 있는 값 | 정해진 몇 가지 값 | 종류가 많고 분포를 모르는 값 | 바깥이 커지는 값 |
| 어느 행이 어디로 갈지 | 적은 사람이 안다 | 적은 사람이 안다 | DBMS가 정한다 | 바깥은 알고 안쪽은 모른다 |
3파티셔닝으로 얻는 것 — 성능·가용성·관리
- 성능: 조건에 맞는 값이 어느 파티션에 있는지 정해져 있어, 그 파티션만 읽고 나머지는 건너뜁니다.
수강_범위 PARTITION (P_높음)처럼 파티션을 짚어 읽을 수도 있습니다. - 가용성: 파티션마다 따로 저장되므로 한 파티션이 망가져도 나머지 파티션의 데이터는 읽을 수 있습니다.
- 관리: 파티션 하나를 통째로 떼어 내거나 더할 수 있습니다. 지난 분기 데이터를 지울 때 행을 하나씩 지우지 않고 파티션을 떼어 냅니다.
대신 치르는 것이 있습니다. 파티션이 많아지면 관리할 조각이 늘고, 기준 열을 쓰지 않는 조건으로 찾으면 모든 파티션을 읽어야 해서 나누지 않았을 때보다 느릴 수 있습니다. 기준 열은 질의가 가장 자주 쓰는 조건으로 고릅니다.
예제
결과 표는 Oracle에서 돌린 결과입니다. 파티셔닝 문법은 DBMS마다 다르고, 아래는 Oracle의 꼴입니다.
USER_TAB_PARTITIONS는 내 표의 파티션 목록을 보여 주는 시스템 카탈로그입니다. NUM_ROWS는 통계 값이라 ANALYZE로 먼저 세어 두어야 채워집니다.
블록마다 처음 데이터에서 시작합니다 — 앞 블록이 만들거나 바꾼 것은 남아 있지 않습니다.
범위 파티셔닝으로 나누기
수강 표의 행을 점수 구간으로 나눕니다.
CREATE TABLE 수강_범위 (
학번 NUMBER,
점수 NUMBER
) PARTITION BY RANGE (점수) (
PARTITION P_낮음
VALUES LESS THAN (70),
PARTITION P_중간
VALUES LESS THAN (90),
PARTITION P_높음
VALUES LESS THAN
(MAXVALUE)
);
INSERT INTO 수강_범위
SELECT 학번, 점수
FROM 수강;
ANALYZE TABLE 수강_범위
COMPUTE STATISTICS;
SELECT PARTITION_NAME,
HIGH_VALUE,
NUM_ROWS
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = '수강_범위'
ORDER BY PARTITION_POSITION;
| PARTITION_NAME | HIGH_VALUE | NUM_ROWS |
|---|---|---|
| P_낮음 | 70 | 4 |
| P_중간 | 90 | 5 |
| P_높음 | MAXVALUE | 3 |
HIGH_VALUE는 그 파티션이 받는 값의 끝이고, 그 값보다 작은 값까지 받습니다. 12행이 4 · 5 · 3으로 나뉘었고, 점수 70은 P_낮음이 아니라 P_중간에 들어갔습니다.
목록 파티셔닝과 그 밖의 칸
학생 표의 행을 학과코드 목록으로 나눕니다. 학과코드가 비어 있는 학생이 있어 DEFAULT 파티션을 둡니다.
CREATE TABLE 학생_목록 (
학번 NUMBER,
학과코드 VARCHAR2(10 CHAR)
) PARTITION BY LIST (학과코드) (
PARTITION P_CS
VALUES ('CS'),
PARTITION P_EE
VALUES ('EE'),
PARTITION P_기타
VALUES (DEFAULT)
);
INSERT INTO 학생_목록
SELECT 학번, 학과코드
FROM 학생;
ANALYZE TABLE 학생_목록
COMPUTE STATISTICS;
SELECT PARTITION_NAME,
HIGH_VALUE,
NUM_ROWS
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = '학생_목록'
ORDER BY PARTITION_POSITION;
| PARTITION_NAME | HIGH_VALUE | NUM_ROWS |
|---|---|---|
| P_CS | 'CS' | 3 |
| P_EE | 'EE' | 3 |
| P_기타 | DEFAULT | 1 |
목록 파티셔닝에서는 HIGH_VALUE 칸에 그 파티션이 받는 목록이 나옵니다.
학과코드가 NULL인 강도현(107)이 P_기타로 갔습니다. DEFAULT 파티션이 없으면 이 행은 들어가지 못합니다.
파티션 하나를 떼어 내기
70점 미만을 모아 둔 파티션을 통째로 지웁니다. 행을 하나씩 찾아 지우지 않습니다.
SQL로 확인하기
점수를 두 구간으로만 나눈 표를 새로 만들어 행을 넣고, 70점 미만 파티션을 떼어 냅니다.
CREATE TABLE 수강_범위 (
학번 NUMBER,
점수 NUMBER
)
PARTITION BY RANGE (점수) (
PARTITION P_낮음
VALUES LESS THAN (70),
PARTITION P_나머지
VALUES LESS THAN
(MAXVALUE)
);
INSERT INTO 수강_범위
SELECT 학번, 점수
FROM 수강;
ALTER TABLE 수강_범위
DROP PARTITION P_낮음;
SELECT COUNT(*) AS 남은행
FROM 수강_범위;| 남은행 |
|---|
| 8 |
12행에서 P_낮음의 4행이 한 번에 사라져 8행이 남았습니다.
DELETE로 지우면 행마다 로그를 쓰고 인덱스도 고쳐야 하지만, 파티션을 떼어 내면 조각 하나를 버리는 일이라 훨씬 빠릅니다.
시험 포인트
- 필기: 파티셔닝은 한 표의 행을 기준 열의 값으로 여러 파티션에 나눠 저장하는 것이고, 질의는 표 하나처럼 씁니다. 범위는 값이 든 구간으로, 목록은 값이 적힌 목록으로, 해시는 값을 계산한 결과로 파티션을 고르고, 조합은 한 기준으로 나눈 파티션 안을 다른 기준으로 다시 나누고, 안쪽 조각을 서브파티션이라 합니다. 해시만 어느 행이 어디로 갈지 사람이 정하지 않습니다. 얻는 것은 성능(필요한 파티션만 읽는다)·가용성(한 파티션이 망가져도 나머지를 쓴다)·관리(파티션 단위로 더하고 떼어 낸다) 셋입니다.
- 실기: 이름은 범위 파티셔닝(Range Partitioning), 목록 파티셔닝(List Partitioning), 해시 파티셔닝(Hash Partitioning), 조합 파티셔닝(Composite Partitioning)으로 씁니다.
확인 문제
1. 목록 파티셔닝(list partitioning)으로 나누기에 알맞은 상황을 고르세요.
- ① 주문일이 2024년부터 2026년까지 이어진 표를 해마다 한 조각씩 나눕니다.
- ② 지점 표를 지점이 있는 도시 '서울'·'부산'·'대구' 셋으로 나눕니다.
- ③ 회원번호가 수백만 가지인 표를 네 조각에 고르게 흩습니다.
- ④ 연도로 나눈 조각이 너무 커서, 조각마다 안을 다시 넷으로 나눕니다.
정답
②입니다. 도시 이름은 순서가 없고 정해진 몇 가지뿐이라, 파티션마다 받을 값을 목록으로 적어 두는 목록 파티셔닝이 맞습니다.
- ① 연도는 순서가 있는 값이고 '2025년 이상 2026년 미만'처럼 구간으로 적습니다. 범위 파티셔닝입니다.
- ③ 값의 종류가 너무 많아 목록을 적을 수 없고, 고르게 흩는 것이 목적입니다. 해시 파티셔닝입니다.
- ④ 한 기준으로 나눈 파티션 안을 다른 기준으로 다시 나누는 것은 조합 파티셔닝입니다.
2. 다음 (가)~(라)에 맞는 이름을 한글 이름과 영문으로 함께 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
- (가) 파티션 수만 넷으로 적어 두었습니다. 어느 행이 어느 파티션에 들어갔는지는 넣어 보고 세어 봐야 알았습니다.
- (나) 파티션마다 '1,000만 원 미만'·'1,000만 원 이상'처럼 받을 구간을 적었습니다.
- (다) 파티션마다 받을 값을 '정상'·'해지'처럼 적어 두고, 적지 않은 값이 들어오면 그 밖의 파티션이 받게 했습니다.
- (라) 먼저 분기로 나눈 다음, 분기마다 그 안을 지점 코드로 한 번 더 나누었습니다.
정답
받는 답: (가) 해시 파티셔닝(Hash Partitioning) · (나) 범위 파티셔닝(Range Partitioning) · (다) 목록 파티셔닝(List Partitioning) · (라) 조합 파티셔닝(Composite Partitioning)
(가)의 실마리는 '수만 적고 어디로 갈지는 모른다', (나)는 '받을 구간을 적었다', (다)는 '받을 값을 적고 그 밖을 따로 받는다', (라)는 '나눈 안을 다시 나눈다'입니다.
- 틀리는 꼴 범위 파티셔닝 — (다)에: 값이 어느 구간에 드는지로 고릅니다. '정상'·'해지'는 순서가 없어 구간을 적을 수 없습니다.
- 틀리는 꼴 목록 파티셔닝 — (가)에: 파티션마다 받을 값을 적어 둡니다. (가)는 받을 값을 적지 않고 수만 적었습니다.
- 틀리는 꼴 서브파티션 — (라)에: 조합 파티셔닝에서 안쪽에 생긴 조각의 이름이고, 나누는 방식의 이름이 아닙니다.
3. 학생 표를 학년으로 나눕니다. 학년이 3보다 작은 행을 P_저학년, 나머지를 P_고학년에 둡니다. (1) 학번과 학년만 담은 표를 이렇게 만들어 행을 넣고 파티션마다 행 수를 구하는 문장을 쓰고 결과를 쓰세요. (2) WHERE 학년 = 2로 찾으면 어느 파티션을 읽는지 쓰세요. 답을 끝까지 쓴 뒤 정답을 펼치세요.
정답
받는 답:
CREATE TABLE 학생_학년 (
학번 NUMBER,
학년 NUMBER
)
PARTITION BY RANGE (학년) (
PARTITION P_저학년
VALUES LESS THAN (3),
PARTITION P_고학년
VALUES LESS THAN
(MAXVALUE)
);
INSERT INTO 학생_학년
SELECT 학번, 학년
FROM 학생;
ANALYZE TABLE 학생_학년
COMPUTE STATISTICS;
SELECT PARTITION_NAME,
NUM_ROWS
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME
= '학생_학년'
ORDER BY
PARTITION_POSITION;| PARTITION_NAME | NUM_ROWS |
|---|---|
| P_저학년 | 4 |
| P_고학년 | 3 |
(1) P_저학년 4행(1학년 둘·2학년 둘), P_고학년 3행(모두 3학년)입니다. (2) P_저학년만 읽습니다. 학년 2는 3보다 작아 P_저학년에만 있을 수 있고, P_고학년은 읽지 않습니다.
더 보기: 같은 파티션 안에서 '자주 같이 읽는 행을 모으는' 일은 다음 레슨 클러스터링과 데이터 지역화에서 봅니다.