카테고리 : MySQL/기술노트

Partition pruning과 실행 계획: 파티션이 성능을 높이지 못하는 경우

MySQL의 Partition pruning과 인덱스 접근을 분리해 실행 계획을 읽고, 파티션을 나누어도 조회가 빨라지지 않는 원인과 검증 절차를 정리한다.

저자: MySQL 기술 노트 작성: 2026.10.19 약 9분 5,270자
다운로드

파티션을 월별로 나누었는데 주문 조회가 빨라지지 않는 경우가 있다. 파티션이 많아졌다는 사실과 쿼리가 적은 데이터를 읽는다는 사실은 다르다. 날짜 조건 없이 주문 번호만 찾으면 여러 파티션의 인덱스를 탐색할 수 있고, 한 달만 읽더라도 그 안의 수천만 행을 순차적으로 조사할 수 있다.

이 글은 어느 파티션을 제외했는가와 남은 파티션에서 어떻게 행을 찾는가를 분리해 설명한다. 대상은 MySQL 8.0 이상 InnoDB이며, SQL 실행 결과는 폐기용 MySQL 8.0 인스턴스에서 확인한 것이다. MySQL 8.4의 공식 문서와 개념을 대조했지만 8.4 및 Aurora에서 같은 실행 계획과 성능을 실측했다는 의미는 아니다.

1. Pruning은 인덱스가 아니라 검색 대상의 축소다

Partition pruning은 조건을 만족하는 행이 존재할 수 없는 파티션을 실행 대상에서 제외하는 최적화다. 예를 들어 날짜 컬럼을 기준으로 월별 RANGE COLUMNS 파티션을 만들고 2월의 반개방 구간을 조회하면, 옵티마이저는 경계 정보로 1월·3월·4월 파티션을 제외할 수 있다. 제외 여부를 판단하기 위해 다른 파티션의 업무 행을 일일이 읽는 것은 아니다.

그러나 2월 파티션을 선택한 뒤의 접근 방식은 별도 문제다. 고객 번호 인덱스가 없다면 2월 안에서 고객을 찾는 데 전체 스캔이 필요할 수 있다. 반대로 주문 번호 인덱스가 있어도 날짜 조건이 없으면 파티션마다 주문 번호를 탐색해야 할 수 있다. 일반적인 InnoDB 파티션 테이블의 인덱스는 파티션별로 구성되므로, 모든 파티션의 키를 한 번에 찾는 전역 보조 인덱스가 생긴다고 생각하면 안 된다.

flowchart TD
    A[SQL 조건과 파티션 경계] --> B{대상 파티션을 좁힐 수 있는가}
    B -->|가능| C[일부 파티션 선택]
    B -->|불가능| D[전체 파티션 선택]
    C --> E[선택한 파티션의 행 접근]
    D --> E
    E --> F{적절한 인덱스 접근이 가능한가}
    F -->|가능| G[키 조회 또는 범위 조회]
    F -->|불가능| H[전체 또는 인덱스 전체 스캔]
    G --> I[잔여 조건 처리와 조인·정렬·집계]
    H --> I

그림은 진단을 위한 개념적 분리다. 옵티마이저 내부에서 모든 단계가 이 순서대로 한 번씩만 수행된다는 뜻은 아니다. 핵심은 pruning 성공이 인덱스 효율, 정렬 제거, 조인 비용 감소까지 보장하지 않는다는 점이다.

2. 실행 계획에서 서로 다른 열을 함께 읽는다

EXPLAIN FORMAT=TRADITIONAL에서 다음 항목을 한 묶음으로 본다. 형식을 명시하면 서버 기본 EXPLAIN 형식에 덜 의존한다.

  • partitions: 접근 대상으로 남은 파티션 목록이다. 실제 디스크 읽기 횟수나 병렬 실행 수가 아니다.
  • type: ALL이면 선택된 파티션 범위의 전체 스캔, index이면 인덱스 전체 스캔, ref·range이면 키 또는 범위 접근을 나타낸다.
  • key, key_len: 실제 선택한 인덱스와 접근에 사용한 키 길이다. 인덱스 이름이 보인다는 이유만으로 적은 행을 읽는다고 판단하지 않는다.
  • rows, filtered: 추정 접근 행 수와 조건 통과 비율이다. InnoDB의 rows는 정확한 행 수가 아니다. 이미 계획에 제시된 값을 파티션 개수로 무조건 다시 곱하지 않는다.
  • Extra: Using where, Using index, Using filesort 등 후속 처리 신호다. Using index는 주로 커버링을 뜻하며 pruning 성공 표시가 아니다.

EXPLAIN ANALYZE는 SELECT를 실제로 실행한다. 실제 iterator의 행 수와 반복 횟수는 추정치와 비교하는 데 유용하지만, 운영의 무거운 집계에 무심코 적용하면 실제 부하를 발생시킨다. 우선 일반 EXPLAIN을 보고, 부하를 통제할 수 있는 환경에서 실행 분석으로 넘어간다.

3. 재현 데이터: 월별 파티션과 고객별 주문

다음 예제는 전용 실습 스키마에서 위에서 아래로 실행한다. 같은 테이블을 이어 사용하므로 중간 블록만 따로 실행하면 안 된다. 마지막 블록에서 정리한다. 날짜는 고정된 설명용 데이터이며 실제 업무 기록이 아니다.

각 월에 1,000행을 만들고 고객 번호를 100종으로 분산한다. 초기 기본 키는 (id, occurred_on)뿐이다. 파티션 컬럼을 기본 키에 포함하지만 날짜가 두 번째이므로 날짜 조건만으로 기본 키의 일반적인 선두 범위 탐색을 할 수는 없다. 기본 키가 보장하는 유일성도 id 단독이 아니라 두 컬럼의 조합이다.

CREATE TABLE pruning_orders (
    id BIGINT NOT NULL,
    occurred_on DATE NOT NULL,
    customer_id INT NOT NULL,
    amount INT NOT NULL,
    PRIMARY KEY (id, occurred_on)
) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS (occurred_on) (
    PARTITION p202601 VALUES LESS THAN ('2026-02-01'),
    PARTITION p202602 VALUES LESS THAN ('2026-03-01'),
    PARTITION p202603 VALUES LESS THAN ('2026-04-01'),
    PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
INSERT INTO pruning_orders (id, occurred_on, customer_id, amount)
WITH digits AS (
    SELECT 0 AS d UNION ALL SELECT 1 UNION ALL SELECT 2
    UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
    UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
    UNION ALL SELECT 9
), months AS (
    SELECT 0 AS m UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
), seq AS (
    SELECT a.d + 10*b.d + 100*c.d AS n
    FROM digits a CROSS JOIN digits b CROSS JOIN digits c
)
SELECT m*1000+n+1,
       DATE_ADD(DATE_ADD('2026-01-01', INTERVAL m MONTH), INTERVAL MOD(n,28) DAY),
       MOD(n,100), 100+MOD(n,50)
FROM months CROSS JOIN seq;
ANALYZE TABLE pruning_orders;
SELECT COUNT(*) AS total_rows,
       SUM(occurred_on >= '2026-02-01' AND occurred_on < '2026-03-01') AS feb_rows
FROM pruning_orders;

실행 결과(MySQL 8.0.x):

아래는 준비 SQL의 긴 원문과 ANALYZE 상태 표를 줄인 발췌다. 실제 검증 버전은 MySQL 8.0.46이다.

mysql> CREATE TABLE pruning_orders ...;
Query OK, 0 rows affected (0.01 sec)

mysql> INSERT INTO pruning_orders ...;
Query OK, 4000 rows affected (0.02 sec)
Records: 4000  Duplicates: 0  Warnings: 0

mysql> SELECT COUNT(*) AS total_rows, SUM(...) AS feb_rows FROM pruning_orders;
+------------+----------+
| total_rows | feb_rows |
+------------+----------+
|       4000 |     1000 |
+------------+----------+
1 row in set (0.00 sec)

이후 비교에서는 조건 변경과 인덱스 추가의 영향을 나누어 살펴본다. OR 및 수동 파티션 지정 예제는 검색 의미 자체가 달라지는 대비이므로 동일 결과의 성능 비교가 아니다. 결과에 표시되는 비용과 시간은 한 번의 소규모 검증 실행값이며 운영 성능의 기준값이 아니다.

4. Pruning에 성공해도 파티션 내부를 전체 스캔하는 경우

EXPLAIN FORMAT=TRADITIONAL
SELECT SUM(amount)
FROM pruning_orders
WHERE occurred_on >= '2026-02-01'
  AND occurred_on < '2026-03-01'
  AND customer_id = 42;

실행 결과(MySQL 8.0.x):

mysql> EXPLAIN FORMAT=TRADITIONAL
    -> SELECT SUM(amount)
    -> FROM pruning_orders
    -> WHERE occurred_on >= '2026-02-01'
    ->   AND occurred_on < '2026-03-01'
    ->   AND customer_id = 42;

+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table          | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | pruning_orders | p202602    | ALL  | NULL          | NULL | NULL    | NULL | 1000 |     1.11 | Using where |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.00 sec)

대상이 p202602 하나가 되더라도 초기 상태에는 고객 번호로 행을 찾는 인덱스가 없다. 결과의 type과 key를 확인해야 한다. 이 조건에서는 2월 안의 행을 조사한 뒤 고객 조건으로 걸러내므로, pruning과 선택적인 인덱스 접근은 같은 의미가 아니다.

월별 데이터가 비슷하게 분포한다면 한 달로 제한하여 전체 기간을 읽는 것보다 작업을 줄일 가능성이 있다. 그러나 “4개 파티션에서 1개가 되었으니 4배 빠르다”고 단정할 수 없다. 특정 월에 몰린 데이터, Buffer Pool에 있는 페이지, 행 너비, 남아 있는 집계·네트워크 처리는 별도로 평가해야 한다.

5. 인덱스를 사용해도 모든 파티션을 대상으로 삼는 경우

EXPLAIN FORMAT=TRADITIONAL
SELECT amount FROM pruning_orders WHERE id = 1043;
EXPLAIN FORMAT=TRADITIONAL
SELECT amount FROM pruning_orders
WHERE id = 1043 AND occurred_on = '2026-02-15';

실행 결과(MySQL 8.0.x):

mysql> EXPLAIN FORMAT=TRADITIONAL
    -> SELECT amount FROM pruning_orders WHERE id = 1043;

+----+-------------+----------------+----------------------------------+------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table          | partitions                       | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------------+----------------------------------+------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | pruning_orders | p202601,p202602,p202603,p_future | ref  | PRIMARY       | PRIMARY | 8       | const |    1 |   100.00 | NULL  |
+----+-------------+----------------+----------------------------------+------+---------------+---------+---------+-------+------+----------+-------+
1 row in set (0.00 sec)

mysql> EXPLAIN FORMAT=TRADITIONAL
    -> SELECT amount FROM pruning_orders
    -> WHERE id = 1043 AND occurred_on = '2026-02-15';

+----+-------------+----------------+------------+-------+---------------+---------+---------+-------------+------+----------+-------+
| id | select_type | table          | partitions | type  | possible_keys | key     | key_len | ref         | rows | filtered | Extra |
+----+-------------+----------------+------------+-------+---------------+---------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | pruning_orders | p202602    | const | PRIMARY       | PRIMARY | 11      | const,const |    1 |   100.00 | NULL  |
+----+-------------+----------------+------------+-------+---------------+---------+---------+-------------+------+----------+-------+
1 row in set (0.00 sec)

첫 쿼리는 기본 키의 선두 컬럼으로 검색할 수 있지만 날짜 정보는 없다. 두 번째 쿼리는 날짜로 대상 파티션을 좁히고 기본 키 전체의 일치 조건도 제공한다. 전체 파티션 대상이어도 전체 행 스캔은 아닐 수 있고, 파티션 하나만 선택해도 접근 행은 많을 수 있다. 이 구별이 장애 조사에서 중요하다.

다만 두 번째 형태로 바꾸려면 호출자가 올바른 날짜를 알고 있어야 한다. 응답을 빠르게 만들려고 “최근 한 달” 조건을 임의로 추가하면 과거에 존재하는 주문을 놓친다. 전체 기간의 ID 검색이 주요 접근 경로라면 비파티션 테이블의 적절한 키 설계와 비교하거나, 정확한 배치 정보를 알려주는 별도 구조가 필요하다.

6. 함수 조건과 OR가 경계 추론을 방해하는 경우

월을 문자열로 변환하여 비교하는 조건과 원래 DATE 값에 범위를 주는 조건을 비교한다.

EXPLAIN FORMAT=TRADITIONAL
SELECT SUM(amount) FROM pruning_orders
WHERE DATE_FORMAT(occurred_on, '%Y-%m') = '2026-02';
EXPLAIN FORMAT=TRADITIONAL
SELECT SUM(amount) FROM pruning_orders
WHERE occurred_on >= '2026-02-01' AND occurred_on < '2026-03-01';
EXPLAIN FORMAT=TRADITIONAL
SELECT SUM(amount) FROM pruning_orders
WHERE (occurred_on >= '2026-02-01' AND occurred_on < '2026-03-01')
   OR customer_id = 42;

실행 결과(MySQL 8.0.x):

세 EXPLAIN에서 partitions, type, rows 열만 순서대로 발췌했다. SQL 프롬프트도 줄였으며 추정 행 수는 이 검증 데이터의 값이다.

mysql> EXPLAIN ... WHERE DATE_FORMAT(occurred_on, '%Y-%m') = '2026-02';
+--------------------------------+------+------+
| partitions                     | type | rows |
+--------------------------------+------+------+
| p202601,p202602,p202603,p_future | ALL  | 4000 |
+--------------------------------+------+------+

mysql> EXPLAIN ... WHERE occurred_on >= '2026-02-01' AND occurred_on < '2026-03-01';
+------------+------+------+
| partitions | type | rows |
+------------+------+------+
| p202602    | ALL  | 1000 |
+------------+------+------+

mysql> EXPLAIN ... WHERE (날짜 범위) OR customer_id = 42;
+--------------------------------+------+------+
| partitions                     | type | rows |
+--------------------------------+------+------+
| p202601,p202602,p202603,p_future | ALL  | 4000 |
+--------------------------------+------+------+

첫 조건은 이 테이블의 파티션 정의에 직접 대응하는 날짜 범위를 주지 않는다. 두 번째의 반개방 구간은 파티션 경계에 대응한다. 세 번째는 2월 이외에도 고객 42의 행이 존재할 수 있으므로 날짜 범위만 보고 다른 파티션을 제외할 수 없다. AND로 고객을 좁히는 경우와 OR로 대상을 넓히는 경우를 혼동하지 않는다.

“함수를 쓰면 반드시 pruning이 안 된다”는 일반화도 잘못이다. MySQL은 YEAR(), TO_DAYS() 등을 사용한 특정 파티션 식과 조건에 대해 pruning을 지원한다. 판단 대상은 함수의 유무가 아니라 해당 버전에서 파티션 식과 조건을 안전하게 연결할 수 있는가다. 실제 정의와 실제 SQL을 함께 놓고 EXPLAIN으로 확인한다.

DATETIME의 월별 검색에서도 다음 달 첫날을 제외하는 반개방 구간을 쓰면 마지막 날의 시각이나 소수 초를 누락하기 어렵다. 다만 TIMESTAMP의 시간대 변환, 매개변수 자료형, 암묵적 변환까지 이 DATE 예제로 검증한 것은 아니다. Prepared statement나 조인 조건도 애플리케이션이 사용하는 실제 실행 형태에서 계획을 확인한다.

7. 남은 파티션에 적절한 인덱스를 제공한다

ALTER TABLE pruning_orders
    ADD INDEX ix_customer_date (customer_id, occurred_on);
ANALYZE TABLE pruning_orders;
EXPLAIN FORMAT=TRADITIONAL
SELECT SUM(amount)
FROM pruning_orders
WHERE occurred_on >= '2026-02-01'
  AND occurred_on < '2026-03-01'
  AND customer_id = 42;
EXPLAIN ANALYZE
SELECT SUM(amount)
FROM pruning_orders
WHERE occurred_on >= '2026-02-01'
  AND occurred_on < '2026-03-01'
  AND customer_id = 42;
SELECT SUM(amount) AS total_amount, COUNT(*) AS matched_rows
FROM pruning_orders
WHERE occurred_on >= '2026-02-01'
  AND occurred_on < '2026-03-01'
  AND customer_id = 42;

실행 결과(MySQL 8.0.x):

긴 결과를 줄이기 위해 ANALYZE 상태 표를 생략하고, Traditional EXPLAIN의 핵심 열만 발췌했다. TREE 결과는 테두리와 긴 범위·조건 표현을 줄였으며 실제 행 수와 반복 횟수는 유지했다.

mysql> ALTER TABLE pruning_orders ADD INDEX ix_customer_date (customer_id, occurred_on);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> EXPLAIN FORMAT=TRADITIONAL SELECT SUM(amount) ...;
+------------+-------+------------------+------+-----------------------+
| partitions | type  | key              | rows | Extra                 |
+------------+-------+------------------+------+-----------------------+
| p202602    | range | ix_customer_date |   10 | Using index condition |
+------------+-------+------------------+------+-----------------------+

mysql> EXPLAIN ANALYZE SELECT SUM(amount) ...;
-> Aggregate: sum(pruning_orders.amount)
   (cost=5.76 rows=1) (actual time=0.0372..0.0373 rows=1 loops=1)
    -> Index range scan on pruning_orders using ix_customer_date
       [범위·index condition 표현 생략]
       (cost=4.76 rows=10) (actual time=0.0301..0.0325 rows=10 loops=1)

mysql> SELECT SUM(amount) AS total_amount, COUNT(*) AS matched_rows ...;
+--------------+--------------+
| total_amount | matched_rows |
+--------------+--------------+
|         1420 |           10 |
+--------------+--------------+
1 row in set (0.00 sec)

검증에서는 p202602가 그대로 유지되면서 접근이 ALL에서 range로 바뀌었다. 인덱스 범위 스캔의 실제 출력은 10행이며 집계값은 1,420이다. 인덱스 추가 전의 rows=1000은 추정값이므로, 이 결과만으로 실제 물리 읽기가 정확히 100분의 1로 줄었다고 주장하지 않는다.

이 비교에서는 파티션을 늘리지 않고 고객의 동등 조건과 날짜 범위에 대응하는 인덱스를 추가한다. partitions가 같아도 접근 방식과 실제 입력 행 수가 바뀐다면, 개선의 주된 원인은 파티션 개수가 아니라 인덱스 접근에 있다.

amount는 이 보조 인덱스에 포함하지 않았으므로 집계값을 얻으려면 행을 참조해야 한다. 읽기 빈도가 높으면 amount를 포함한 커버링 인덱스도 후보지만, 인덱스 용량, 쓰기 증폭, Buffer Pool 점유와 비교해야 한다. 작은 예제의 실행 시간 차이를 운영의 단축 비율이나 스토리지 I/O 절감량으로 사용해서는 안 된다.

8. 수동 파티션 지정은 성능 힌트가 아니다

PARTITION (...)은 지정한 부분만 검색 대상으로 삼는 명시적 선택이다. 자동 pruning과 다르며, 잘못 지정해도 다른 파티션으로 다시 검색하지 않는다.

SELECT COUNT(*) AS all_customer_rows
FROM pruning_orders WHERE customer_id = 42;
SELECT COUNT(*) AS feb_customer_rows
FROM pruning_orders PARTITION (p202602) WHERE customer_id = 42;
SELECT PARTITION_NAME, PARTITION_DESCRIPTION
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pruning_orders'
ORDER BY PARTITION_ORDINAL_POSITION;
DROP TABLE pruning_orders;

실행 결과(MySQL 8.0.x):

mysql> SELECT COUNT(*) AS all_customer_rows
    -> FROM pruning_orders WHERE customer_id = 42;

+-------------------+
| all_customer_rows |
+-------------------+
|                40 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) AS feb_customer_rows
    -> FROM pruning_orders PARTITION (p202602) WHERE customer_id = 42;

+-------------------+
| feb_customer_rows |
+-------------------+
|                10 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT PARTITION_NAME, PARTITION_DESCRIPTION
    -> FROM information_schema.PARTITIONS
    -> WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'pruning_orders'
    -> ORDER BY PARTITION_ORDINAL_POSITION;

+----------------+-----------------------+
| PARTITION_NAME | PARTITION_DESCRIPTION |
+----------------+-----------------------+
| p202601        | '2026-02-01'          |
| p202602        | '2026-03-01'          |
| p202603        | '2026-04-01'          |
| p_future       | MAXVALUE              |
+----------------+-----------------------+
4 rows in set (0.00 sec)

mysql> DROP TABLE pruning_orders;

Query OK, 0 rows affected (0.00 sec)

두 건수의 차이는 쿼리 의미가 바뀐 결과이지, 동일한 검색을 빠르게 만든 증거가 아니다. 월 전환, 지연 도착, 과거 데이터 수정이 있는 시스템에서 애플리케이션에 파티션 이름을 고정하면 누락을 정상 응답으로 반환할 위험이 있다.

경계를 확인할 때는 파티션 이름만 믿지 않는다. 첫 RANGE 파티션에는 명시적인 하한이 없고 MAXVALUE는 이후 범위를 함께 받는다. 날짜를 담은 이름과 실제 저장 범위가 일치하는지는 정의와 경계로 판단한다. information_schema.PARTITIONS.TABLE_ROWS를 활용하더라도 InnoDB에서는 추정값이므로 정확한 건수 대조를 대신하지 못한다.

9. 파티션이 성능 개선으로 이어지지 않는 대표 조건

검색 축과 분할 축이 다르다

날짜로 분할한 테이블을 고객 번호나 주문 번호만으로 자주 찾으면, 파티션을 늘릴수록 여러 로컬 인덱스를 다루는 부담이 커질 수 있다. 날짜 파티션은 보존 기간 관리에 유용하더라도 모든 OLTP 검색을 빠르게 하는 수단은 아니다.

선택 범위가 크거나 데이터가 편중되어 있다

전체 기간의 매출 집계, 최신 월에 집중되는 입력, 비대해진 MAXVALUE 파티션에서는 pruning으로 제외할 작업이 적다. 파티션 개수가 아니라 실제로 남는 행과 페이지 양을 평가한다. 작게 나누면 항상 유리한 것도 아니며 관리·DDL·백업 대상 증가를 함께 고려해야 한다.

후속 처리가 주요 병목이다

대량 결과 전송, GROUP BY, ORDER BY, 조인 상대의 비효율적 접근이 지배적이면 pruning만으로 응답 시간이 크게 달라지지 않는다. Using filesort는 추가 정렬 작업을 나타내지만 그 자체로 디스크 유출을 입증하지는 않는다. 실행 계획 전체와 실측 지표를 대조한다.

설계 변경이 다른 비용을 만든다

파티션 키를 모든 고유 키에 포함하는 제약 때문에 기존 ID 단독 유일성이 사라질 수 있다. 기본 키 너비가 커지면 보조 인덱스에도 영향을 준다. 데이터 정합성과 쓰기 비용을 희생한 “검색 개선”이 전체 시스템의 개선이라는 보장은 없다.

10. Aurora에서는 SQL 계층과 스토리지 계층을 분리해 평가한다

Aurora MySQL에서도 파티션 경계와 검색 조건이 대응하는지, 남은 행을 어느 인덱스로 읽는지가 중요한 진단 축이다. 반면 SQL의 사용자 정의 파티션을 Aurora의 분산 스토리지 단위나 reader 자동 부하 분산 단위와 동일시해서는 안 된다.

이 로컬 실험에서는 Aurora의 I/O, reader 캐시, Parallel Query, 버전별 실행 계획 차이를 측정하지 않았다. Aurora에서는 대상 엔진 버전, 인스턴스, writer/reader, 파라미터를 고정하고 비교한다. 일반적인 파티션 정의를 추가했다는 이유만으로 병렬 처리가 활성화되었다고 판단하지 않는다.

11. 운영 비교 절차와 체크리스트

비교는 “파티션 적용 전후”, “조건 변경 전후”, “인덱스 추가 전후”를 분리한다. 한꺼번에 바꾸면 어느 요소가 개선을 만들었는지 판단하기 어렵다. 데이터 분포, 인덱스, 반환 행, 연결 조건을 맞추고 준비 실행과 반복 측정을 수행한다. 속도보다 먼저 검색 결과가 같은지 확인한다.

  • partitions와 type·key
  • rows

12. 정리

Partition pruning은 “읽을 필요가 없는 구역을 제외하는” 기능이지 남은 구역의 효율적 검색까지 보장하는 기능이 아니다. partitions, 접근 방식, 인덱스, 후속 처리를 나누어 읽으면 분할 자체의 효과와 인덱스 설계 효과를 구별할 수 있다.

다음으로 살펴볼 대상은 파티션 추가·삭제·재구성을 어떤 경계와 절차로 운영할 것인가다. 검색 계획의 적절성과 데이터 수명주기 관리를 함께 확인해야 파티션이 지속 가능한 운영 설계가 된다.

참고 자료