공공부하자개발 · 영어 학습 노트
SQL
대용량·배치실행 계획·조인·인덱스·파티션·락·대량 처리0/8 완료
  • 01실행 계획 읽기
  • 02조인 방식: Nested Loop, Hash, Sort Merge 와 드라이빙 테이블
  • 03인덱스 튜닝
  • 04통계 정보와 힌트
  • 05파티션: 범위·목록·해시 분할, 파티션 프루닝, 파티션 단위 관리
  • 06대량 INSERT·UPDATE 와 배치 커밋
  • 07락과 데드락
  • 08대용량 삭제·아카이빙
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 대용량·배치 › 05 / 8

파티션: 범위·목록·해시 분할, 파티션 프루닝, 파티션 단위 관리

섹션 6진행 0 / 8
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6정리‹ 이전다음 ›

3. 코드 예제

소스: sql-src/batch_05_partition/01_manual_split.sql, 02_key_choice.sql, 03_drop_vs_delete.sql.

이 절의 sql 은 모두 H2 로 실행한 평문 SQL 입니다. 월별 표 여러 개와 UNION ALL 뷰로 파티션처럼 나눈 구조를 흉내 낸 것이며, 실제 파티션이 아닙니다. 실제 파티션은 표 하나를 DB 가 알아서 나누고, 여기서는 사람이 표를 나눠 놓고 뷰로 묶습니다.

예제 1: 월별 표와 UNION ALL 뷰 (설명용 흉내)

1월부터 4월까지 월별 표를 만들고 뷰로 묶습니다. 표마다 그 달의 날짜만 들어 있습니다. CREATE TABLE 과 INSERT 는 네 표가 같은 형태이며 행 수만 다릅니다(1월 1,000, 2월 800, 3월 1,200, 4월 500).

sql
CREATE VIEW orders_all AS
SELECT * FROM orders_2025_01
UNION ALL
SELECT * FROM orders_2025_02
UNION ALL
SELECT * FROM orders_2025_03
UNION ALL
SELECT * FROM orders_2025_04;

표별 건수와 날짜 범위를 확인합니다.

sql
SELECT
       '2025_01' AS part
     , COUNT(*) AS cnt
     , MIN(ord_date) AS min_date
     , MAX(ord_date) AS max_date
  FROM orders_2025_01
 UNION ALL
SELECT '2025_02', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_02
 UNION ALL
SELECT '2025_03', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_03
 UNION ALL
SELECT '2025_04', COUNT(*), MIN(ord_date), MAX(ord_date) FROM orders_2025_04;
text
PART    | CNT  | MIN_DATE   | MAX_DATE
--------+------+------------+-----------
2025_01 | 1000 | 2025-01-01 | 2025-01-31
2025_02 | 800  | 2025-02-01 | 2025-02-28
2025_03 | 1200 | 2025-03-01 | 2025-03-31
2025_04 | 500  | 2025-04-01 | 2025-04-30
(4행)

각 표의 최소·최대 날짜가 그 달 안에 들어 있습니다. 이 범위가 파티션의 경계 값과 같은 역할을 합니다. 뷰로 전체를 세면 네 표의 합입니다.

sql
SELECT COUNT(*) AS total_rows FROM orders_all;
text
TOTAL_ROWS
----------
3500
(1행)

예제 2: 3월만 읽으면 되는 이유

3월 조건으로 뷰를 조회한 결과와, 3월 표만 직접 조회한 결과를 비교합니다.

sql
SELECT
       COUNT(*) AS cnt
     , SUM(amt) AS sum_amt
  FROM orders_all
 WHERE ord_date >= DATE '2025-03-01'
   AND ord_date < DATE '2025-04-01';
text
CNT  | SUM_AMT
-----+--------
1200 | 5773800
(1행)
sql
SELECT
       COUNT(*) AS cnt
     , SUM(amt) AS sum_amt
  FROM orders_2025_03;
text
CNT  | SUM_AMT
-----+--------
1200 | 5773800
(1행)

두 결과가 같습니다. 3월 조건에 맞는 행은 3월 표에만 있으므로 나머지 세 표를 읽지 않아도 답이 같습니다. 이것이 프루닝의 원리입니다. 실제 파티션은 DB 가 이 판단을 자동으로 하고, 이 뷰는 그 판단을 사람이 대신하는 흉내입니다.

조건에 걸리는 행이 표마다 몇 개인지 세면 어느 표를 건너뛸 수 있는지 보입니다.

sql
SELECT
       part
     , cnt
  FROM (
        SELECT '2025_01' AS part, COUNT(*) AS cnt FROM orders_2025_01 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
         UNION ALL
        SELECT '2025_02', COUNT(*) FROM orders_2025_02 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
         UNION ALL
        SELECT '2025_03', COUNT(*) FROM orders_2025_03 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
         UNION ALL
        SELECT '2025_04', COUNT(*) FROM orders_2025_04 WHERE ord_date >= DATE '2025-03-01' AND ord_date < DATE '2025-04-01'
       ) t
 ORDER BY part;
text
PART    | CNT
--------+-----
2025_01 | 0
2025_02 | 0
2025_03 | 1200
2025_04 | 0
(4행)

3월 표만 건수가 있고 나머지는 0 입니다. 0 인 표는 읽어도 얻는 것이 없는 표이고, 프루닝은 이런 표를 미리 건너뜁니다. 이 쿼리 자체는 모든 표를 읽었으므로 프루닝의 예가 아니라 어느 표가 건너뛸 대상인지 보이는 예입니다.

예제 3: 키 후보별 분포

같은 4,000행을 세 가지 키로 나눌 때 조각 크기가 어떻게 되는지 계산합니다. 범위·목록·해시가 각각 어떤 분포를 만드는지 보는 것입니다. 데이터는 orders 4,000행이고 날짜는 1월부터 4월까지, 지점은 다섯 곳입니다.

범위 키는 날짜의 월입니다.

sql
SELECT
       EXTRACT(MONTH FROM ord_date) AS mon
     , COUNT(*) AS cnt
  FROM orders
 GROUP BY EXTRACT(MONTH FROM ord_date)
 ORDER BY mon;
text
MON | CNT
----+-----
1   | 1053
2   | 934
3   | 1023
4   | 990
(4행)

월별로 크기가 조금씩 다릅니다. 달마다 일수와 데이터량이 달라 완전히 같을 수는 없지만 비슷한 크기입니다.

목록 키는 지점입니다.

sql
SELECT
       branch
     , COUNT(*) AS cnt
  FROM orders
 GROUP BY branch
 ORDER BY cnt DESC;
text
BRANCH | CNT
-------+-----
서울   | 2000
부산   | 1000
대구   | 600
광주   | 200
제주   | 200
(5행)

서울이 절반입니다. 목록 파티션은 값이 몰리면 조각 크기가 크게 달라집니다. 서울 조각만 커서 조각 나누기의 효과가 줄어듭니다.

해시 키는 id 를 4 로 나눈 나머지입니다.

sql
SELECT
       MOD(id, 4) AS bucket
     , COUNT(*) AS cnt
  FROM orders
 GROUP BY MOD(id, 4)
 ORDER BY bucket;
text
BUCKET | CNT
-------+-----
0      | 1000
1      | 1000
2      | 1000
3      | 1000
(4행)

네 조각이 정확히 같습니다. 해시는 고르게 나누는 데 강합니다. 대신 값이 어느 조각에 있는지가 해시로 정해져, 기간 조회 같은 범위 조건으로는 조각을 줄일 수 없습니다. 해시 키는 보통 특정 값 하나를 찾는 조회에 맞습니다.

예제 4: 표 비우기 vs DELETE

1월 데이터를 지우는 두 방법을 봅니다. 큰 표 하나에서 DELETE 로 지우는 방법과, 월별 표에서 그 달 표를 비우는 방법입니다. 결과 건수만 비교하고 속도는 측정하지 않았습니다.

sql
DELETE FROM orders_big WHERE ord_date < DATE '2025-02-01';
SELECT
       COUNT(*) AS big_rows
     , MIN(ord_date) AS min_date
  FROM orders_big;
text
BIG_ROWS | MIN_DATE
---------+-----------
2947     | 2025-02-01
(1행)

4,000행에서 1월 1,053행이 지워졌고 남은 최소 날짜는 2월 1일입니다. DELETE 는 조건에 맞는 행을 찾아 한 행씩 지우고 로그를 남기고 인덱스도 고칩니다. 행이 수억이면 이 작업이 오래 걸립니다.

월별 표에서는 1월 표를 비우면 끝납니다.

sql
TRUNCATE TABLE orders_2025_01;
SELECT COUNT(*) AS jan_rows FROM orders_2025_01;
text
JAN_ROWS
--------
0
(1행)

TRUNCATE 는 행을 찾지 않고 표의 데이터 공간을 통째로 비웁니다. 지울 행이 몇 개인지와 상관없이 일이 작습니다. 이것이 파티션 단위 관리의 이득입니다. 2월 표는 DROP 으로 표째 없앨 수도 있습니다.

sql
DROP TABLE orders_2025_02;
SELECT COUNT(*) AS tables_left FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'ORDERS_2025_02';
text
TABLES_LEFT
-----------
0
(1행)
참고

이 비교에서 두 방법의 결과 건수만 확인했습니다. 실제 시간 차이는 행 수, 인덱스, 로그 설정에 따라 크게 달라 이 레슨에서 측정하지 않았고, 원리(찾아서 지움 대 통째로 비움)만 설명했습니다.

예제 직접 실행

아래 폴더의 SQL 파일을 Git Bash 에서 H2 메모리 DB 로 실행합니다. 방언은 파일 안의 SET MODE 로 바꿉니다.

cd sql-src/batch_05_partition
ls *.sql
bash ../run.sh <파일>.sql
코드 예제
  • 예제 1: 월별 표와 UNION ALL 뷰 (설명용 흉내)
  • 예제 2: 3월만 읽으면 되는 이유
  • 예제 3: 키 후보별 분포
  • 예제 4: 표 비우기 vs DELETE
이전 섹션2 핵심 원리3 / 6다음 섹션4 응용 변형 예제