반응형

MySQL 파티션 테이블 가이드

들어가며: 파티션의 필요성

대규모 데이터베이스를 운영하다 보면 마치 거대한 도서관을 관리하는 것과 같은 어려움을 겪게 됩니다. 수백만 건의 데이터를 하나의 테이블에서 관리한다면, 책장 전체를 뒤져야 하는 것처럼 검색 시간이 오래 걸리고 관리도 어려워집니다. 이러한 문제를 해결하기 위해 MySQL은 파티셔닝이라는 강력한 기능을 제공합니다.

파티션 테이블의 이해

파티션 테이블은 마치 도서관의 책을 주제별로 나누어 관리하는 것과 같습니다. 하나의 큰 테이블을 여러 개의 작은 물리적 조각(파티션)으로 나누어 저장하지만, 사용자 입장에서는 여전히 하나의 테이블처럼 사용할 수 있습니다.

예를 들어, 온라인 쇼핑몰의 주문 내역을 연도별로 파티셔닝한다고 생각해봅시다:

-- 주문 내역 테이블을 연도별로 파티셔닝하는 예제
-- 실제 운영에서 자주 사용되는 구조입니다
CREATE TABLE orders (
    order_id INT,
    order_date DATE,
    customer_id INT,
    amount DECIMAL(10,2),
    -- 파티션 키가 포함된 PRIMARY KEY
    PRIMARY KEY (order_id, order_date)
) 
PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

이렇게 구성하면 2023년 주문을 조회할 때, MySQL은 p2023 파티션만 검색하면 되므로 성능이 크게 향상됩니다.

파티션의 종류와 실전 활용

1. Range Partitioning (범위 파티셔닝)

시간이나 숫자 범위로 데이터를 나눌 때 사용합니다. 주로 날짜별 로그 데이터나 연령대별 회원 정보 관리에 적합합니다.

-- 예제: 고객의 연령대별 파티셔닝
CREATE TABLE customers (
    id INT,
    birth_year INT,
    name VARCHAR(100),
    email VARCHAR(100),
    PRIMARY KEY (id, birth_year)
)
PARTITION BY RANGE (birth_year) (
    PARTITION p_gen_z VALUES LESS THAN (2000),
    PARTITION p_millenial VALUES LESS THAN (1995),
    PARTITION p_gen_x VALUES LESS THAN (1980),
    PARTITION p_others VALUES LESS THAN MAXVALUE
);

2. List Partitioning (목록 파티셔닝)

지역 코드나 카테고리처럼 고정된 값 목록으로 데이터를 나눌 때 사용합니다:

-- 예제: 지역별 매장 정보 파티셔닝
CREATE TABLE stores (
    store_id INT,
    region_code CHAR(2),
    store_name VARCHAR(100),
    revenue DECIMAL(10,2),
    PRIMARY KEY (store_id, region_code)
)
PARTITION BY LIST (region_code) (
    PARTITION p_capital VALUES IN ('SE', 'GG'),  -- 수도권
    PARTITION p_south VALUES IN ('BS', 'GJ', 'DG'),  -- 남부권
    PARTITION p_central VALUES IN ('DJ', 'GW')   -- 중부권
);

3. Hash Partitioning (해시 파티셔닝)

데이터를 균일하게 분산시키고 싶을 때 사용합니다. 시스템이 자동으로 데이터를 분산시켜줍니다:

-- 예제: 사용자 로그 데이터 해시 파티셔닝
CREATE TABLE user_logs (
    log_id INT,
    user_id INT,
    action_type VARCHAR(50),
    log_time TIMESTAMP,
    PRIMARY KEY (log_id, user_id)
)
PARTITION BY HASH(user_id)
PARTITIONS 4;  -- 4개의 파티션으로 균등하게 분할

파티션 관리와 최적화

파티션 상태 모니터링

-- 파티션별 데이터 분포 확인
SELECT 
    PARTITION_NAME,
    TABLE_ROWS,
    DATA_LENGTH/1024/1024 as 'Data Size (MB)'
FROM 
    INFORMATION_SCHEMA.PARTITIONS
WHERE 
    TABLE_NAME = 'your_table_name';

파티션 유지보수

-- 새로운 파티션 추가
ALTER TABLE orders ADD PARTITION (
    PARTITION p2025 VALUES LESS THAN (2026)
);

-- 오래된 파티션 삭제
ALTER TABLE orders DROP PARTITION p2022;

-- 파티션 재구성
ALTER TABLE orders REORGANIZE PARTITION p_future INTO (
    PARTITION p2025 VALUES LESS THAN (2026),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

실전 운영 팁

  1. 파티션 키 선정: 쿼리에서 자주 사용되는 WHERE 조건을 파티션 키로 선택합니다.
  2. 파티션 수 관리: 너무 많은 파티션은 오히려 성능을 저하시킬 수 있습니다. 일반적으로 테이블당 50개 이하를 권장합니다.
  3. 데이터 분포 모니터링: 특정 파티션에 데이터가 몰리지 않도록 주기적으로 확인합니다.
  4. 백업 전략: 파티션별로 백업을 수행하면 백업/복구 시간을 단축할 수 있습니다.

주의사항

  1. PRIMARY KEY에는 반드시 파티션 키가 포함되어야 합니다.
  2. FOREIGN KEY 제약조건은 파티션 테이블에서 사용할 수 없습니다.
  3. 파티션 간 JOIN 연산은 성능 저하를 초래할 수 있으므로 신중히 설계해야 합니다.
  4. 너무 잦은 파티션 키 값 변경은 성능 문제를 일으킬 수 있습니다.

마치며

파티션 테이블은 대용량 데이터 관리를 위한 강력한 도구이지만, 신중한 설계와 지속적인 관리가 필요합니다. 실제 데이터의 특성과 접근 패턴을 충분히 분석한 후, 적절한 파티셔닝 전략을 선택하시기 바랍니다.

반응형
반응형

파티션 테이블이란?

파티션 테이블은 대용량 데이터를 효과적으로 관리하기 위해 하나의 테이블을 여러 물리적 단위(파티션)로 나누어 저장하는 방식입니다. 논리적으로는 하나의 테이블로 동작하지만, 물리적으로는 데이터가 각 파티션에 분산 저장됩니다. 이를 통해 데이터를 조회하거나 관리할 때 성능과 효율성을 크게 향상시킬 수 있습니다.

파티션 테이블은 특히 Pruning이라는 최적화 기법을 지원합니다. 특정 데이터를 조회할 때 필요한 데이터가 위치한 파티션만 읽어들이는 방식으로, 쿼리 속도를 대폭 개선합니다. 또한, 파티션 테이블은 개발자 입장에서 기존 쿼리를 특별히 수정할 필요가 없으면서도 데이터 관리와 유지보수를 간소화할 수 있는 장점이 있습니다.


1. 파티션 테이블의 장단점

장점

  • 쿼리 성능 향상: 특정 조건에 맞는 데이터만 저장된 파티션을 조회하므로, 전체 테이블을 스캔하지 않아도 됩니다.
  • 가용성 강화: 디스크 장애가 발생해도 문제가 발생한 파티션만 복구하면 되므로 데이터 손실 위험이 줄어듭니다.
  • 효율적 데이터 관리: 파티션 단위로 데이터 삭제, 백업, 아카이빙 등을 수행할 수 있어 관리가 편리합니다.
  • 기존 쿼리와 호환성 유지: 논리적으로 하나의 테이블로 동작하므로, 기존 쿼리를 수정할 필요가 없습니다.
  • 병렬 처리 지원: 조인 작업이나 데이터 처리를 파티션 단위로 병렬 처리하여 성능을 향상시킬 수 있습니다.
  • I/O 부하 분산: 데이터를 물리적으로 분산 저장하므로 디스크 I/O 부하가 감소합니다.

단점

  • 복잡한 관리: 파티션 키 값 변경 시 별도의 관리가 필요하며, 설계와 유지보수가 복잡해질 수 있습니다.
  • 오버헤드 발생: 데이터 입력 시 어느 파티션에 저장할지 결정하는 연산이 추가되며, 인서트 속도가 느려질 수 있습니다.
  • JOIN 비용 증가: 파티션 간 데이터를 조인할 때 성능 저하가 발생할 가능성이 있습니다.
  • 설계 제약: 파티션 기준이 되는 컬럼과 데이터 분포를 잘못 설계하면 성능 저하 및 데이터 불균형 문제가 생길 수 있습니다.

2. 어떤 테이블을 파티션 테이블로 만들어야 할까?

파티션 테이블은 모든 테이블에 적합한 방법은 아니며, 아래 조건을 충족하는 경우 활용 가치가 높습니다.

  • 대규모 데이터: 데이터 양이 방대하고 지속적으로 증가하는 테이블(예: 로그 테이블).
  • 지속적인 데이터 입력: 대량의 데이터가 주기적으로 인서트되는 테이블.
  • 오래된 데이터 관리: 특정 기간이 지나면 삭제하거나 아카이빙해야 하는 데이터가 포함된 경우.
  • 범위 기반 쿼리: 날짜, 시간, 지역 등 특정 조건으로 데이터를 조회하는 경우가 많은 테이블.
  • 장애 복구가 중요한 테이블: 일부 데이터 손상에도 나머지 데이터를 정상적으로 사용해야 하는 경우.

3. 파티션 키 컬럼 (파티셔닝 키)

파티션 키는 데이터를 각 파티션에 분산 저장하는 기준이 되는 컬럼입니다. 적절한 파티션 키를 선택하면 데이터의 균등 분산과 쿼리 성능을 극대화할 수 있습니다.

파티션 키 선정 기준

  • 직관적인 데이터 분리: 날짜, 월, 연도 등 사용자가 데이터를 쉽게 이해하고 구분할 수 있는 컬럼.
  • 균등한 데이터 분포: 특정 파티션에 데이터가 집중되지 않도록 고른 분포가 가능한 컬럼.
  • I/O 효율성: 쿼리 조건에서 자주 사용되며, 데이터 접근 패턴과 부합하는 컬럼.
  • 데이터 관리 용이성: 데이터 삭제, 백업, 이동 시 유리한 컬럼.
  • 피해야 할 컬럼: Primary Key처럼 중복되지 않는 고유값은 분산 효과가 적어 적합하지 않음.

4. 파티션 테이블의 종류

1) Range Partitioning

특정 범위를 기준으로 데이터를 분할하는 방식입니다. 주로 날짜, 숫자 등 연속적인 값을 가진 컬럼에 적합합니다.

  • 장점: 관리가 간단하며, 시간 기반 데이터(로그, 이력 등)에 적합.
  • 단점: 데이터가 특정 범위에 몰릴 경우 부하가 증가.

2) Hash Partitioning

해시 함수를 사용하여 데이터를 고르게 분산하는 방식으로, 값의 분포가 불규칙하거나 예측하기 어려운 경우 유용합니다.

  • 장점: 데이터가 균등하게 분산되어 부하가 고르게 분산.
  • 단점: 파티션 간 데이터 분포를 사용자가 직접 제어할 수 없음.

3) List Partitioning

명시적으로 정의된 특정 값의 목록에 따라 데이터를 분할합니다. 카테고리, 지역 등 그룹화가 필요한 데이터에 적합합니다.

  • 장점: 사용자가 원하는 값에 따라 데이터를 명확히 구분 가능.
  • 단점: 모든 값에 대한 리스트를 정의해야 하므로 설정이 번거로울 수 있음.

4) Composite Partitioning

두 가지 이상의 파티셔닝 방식을 조합하여 사용하는 고급 방식입니다(예: Range + Hash).

  • 장점: 다양한 데이터 요구를 동시에 충족할 수 있음.
  • 단점: 관리 복잡도가 높아지고, 파티션 수가 너무 많아질 위험이 있음.

파티션 테이블은 대규모 데이터 환경에서 성능과 관리 효율성을 높이는 데 매우 유용한 기법입니다. 하지만 데이터의 특성과 사용 패턴을 면밀히 분석해 설계하지 않으면 오히려 성능 저하와 관리 복잡성을 초래할 수 있습니다. 따라서 적절한 파티션 키 선정과 파티션 종류의 활용이 핵심입니다.

※ MySQL 에서 파티션 테이블의 생성 방법을 알고 싶다면 아래 글을 참고해주세요

[MySQL] MySQL 파티션 테이블 가이드

 

[DB] MySQL 파티션 테이블 가이드

MySQL 파티션 테이블 가이드들어가며: 파티션의 필요성대규모 데이터베이스를 운영하다 보면 마치 거대한 도서관을 관리하는 것과 같은 어려움을 겪게 됩니다. 수백만 건의 데이터를 하나의

ethank.tistory.com

 

반응형
반응형

커버링 인덱스(Covering Index)란 무엇인가?

커버링 인덱스는 쿼리가 필요로 하는 모든 컬럼을 포함하는 인덱스를 말합니다. 이 인덱스를 사용하면 테이블의 실제 데이터 페이지에 접근하지 않고도 인덱스만으로 원하는 데이터를 조회할 수 있어 디스크 I/O를 절약할 수 있습니다.

MySQL 공식 문서 정의:

*"쿼리에서 검색되는 모든 컬럼을 포함하는 인덱스입니다. 인덱스 값을 전체 테이블 행을 찾는 포인터로 사용하는 대신, 쿼리는 인덱스 구조에서 값을 반환하여 디스크 I/O를 절약합니다. InnoDB는 MyISAM보다 더 많은 인덱스에 이 최적화 기술을 적용할 수 있습니다. 왜냐하면 InnoDB의 보조 인덱스에는 기본 키 컬럼도 포함되어 있기 때문입니다. InnoDB는 해당 트랜잭션이 끝날 때까지 트랜잭션에 의해 수정된 테이블에 대한 쿼리에 이 기술을 적용할 수 없습니다."*

단일 컬럼 인덱스와 다중 컬럼 인덱스 모두 커버링 인덱스로 활용될 수 있습니다. 적절한 인덱스 설계와 쿼리 작성으로 이 최적화 기법을 최대한 활용할 수 있습니다.


커버링 인덱스를 사용하는 이유

  • 디스크 I/O 감소: 인덱스만으로 데이터를 조회하므로 디스크 접근 횟수가 줄어듭니다.
  • 성능 향상: 디스크 I/O 감소로 쿼리 응답 속도가 빨라집니다.
  • 잠금 경합 감소: 테이블 데이터 페이지에 대한 접근이 줄어들어 잠금 경합이 감소합니다.

클러스터드 인덱스와 비클러스터드 인덱스

클러스터드 인덱스 (Clustered Index)

  • 정의: 테이블의 실제 데이터가 인덱스와 동일한 구조로 저장되는 인덱스입니다.
  • 특징:
    • 테이블 당 하나만 존재합니다.
    • 기본 키(primary key)가 클러스터드 인덱스로 사용됩니다.
  • 장점:
    • 인덱스를 통해 바로 데이터에 접근하므로 조회 속도가 빠릅니다.

비클러스터드 인덱스 (Non-clustered Index)

  • 정의: 인덱스는 별도의 구조로 저장되고, 인덱스 엔트리는 데이터의 물리적 위치를 가리킵니다.
  • 특징:
    • 테이블 당 여러 개의 비클러스터드 인덱스를 가질 수 있습니다.
  • 단점:
    • 인덱스를 통해 데이터를 찾은 후 실제 데이터 페이지를 다시 조회해야 하므로 추가적인 I/O가 발생합니다.

MySQL 공식 문서 인용:

*"클러스터드 인덱스를 통해 행에 접근하는 것은 빠릅니다. 왜냐하면 인덱스 검색이 행 데이터가 있는 페이지로 직접 연결되기 때문입니다. 테이블이 큰 경우, 클러스터드 인덱스 아키텍처는 인덱스 레코드와 다른 페이지에 행 데이터를 저장하는 스토리지 구조와 비교했을 때 디스크 I/O 작업을 절약할 수 있습니다."*


커버링 인덱스 사용과 미사용 시 성능 비교

실험: 100만 건의 데이터로 임시 테이블 생성 후 성능 비교

  1. 데이터 준비

    CREATE TABLE test_table (
        id INT PRIMARY KEY,
        col1 VARCHAR(100),
        col2 VARCHAR(100),
        col3 VARCHAR(100)
    );
    
    INSERT INTO test_table (id, col1, col2, col3)
    SELECT
        t1.number AS id,
        MD5(RAND()) AS col1,
        MD5(RAND()) AS col2,
        MD5(RAND()) AS col3
    FROM
        numbers AS t1  -- numbers 테이블은 1부터 1,000,000까지의 숫자를 가진 테이블이라고 가정
    LIMIT 1000000;
  2. 인덱스 설정

    • 커버링 인덱스 생성

      CREATE INDEX idx_col1_col2 ON test_table (col1, col2);
    • 인덱스 미사용

      • 인덱스를 생성하지 않음.
  3. 쿼리 실행 및 성능 측정

    • 커버링 인덱스 사용 시

      SELECT col1, col2 FROM test_table WHERE col1 = 'some_value';
      • 인덱스만으로 쿼리를 처리하여 빠른 응답 속도를 보입니다.
    • 인덱스 미사용 시

      SELECT col1, col2 FROM test_table WHERE col1 = 'some_value';
      • 전체 테이블 스캔이 발생하여 응답 시간이 느려집니다.
  4. 결과

    • 커버링 인덱스를 사용하면 쿼리 성능이 현저히 향상됩니다.
    • 인덱스를 사용하지 않으면 대량의 데이터를 처리할 때 성능 저하가 발생합니다.

GROUP BY에서의 커버링 인덱스 활용

GROUP BY 절에서도 커버링 인덱스를 활용하여 성능을 개선할 수 있습니다.

  • 인덱스 생성

    CREATE INDEX idx_col1_col2 ON test_table (col1, col2);
  • 쿼리 실행

    SELECT col1, COUNT(*) FROM test_table GROUP BY col1;
  • 설명

    • col1col2를 포함하는 인덱스를 생성하여 GROUP BY 시 인덱스만으로 결과를 도출합니다.
    • 디스크 I/O를 최소화하여 쿼리 성능을 높입니다.

참고 자료

  1. MySQL 공식 문서 - InnoDB Index Types
  2. MySQL 공식 문서 - Covering Indexes
반응형

'Database > My SQL' 카테고리의 다른 글

[DB] MySQL 파티션 테이블 가이드  (0) 2025.01.04
[DB] 파티션 테이블(Partition Table)이란?  (0) 2025.01.04
MySQL Index 정리 및 팁  (0) 2022.05.16
반응형

인덱스(Index)란 무엇인가?

MySQL에서 인덱스는 데이터를 효율적으로 검색하기 위해 사용되는 자료구조입니다. 기본적으로 MySQL은 B-Tree 인덱스를 사용하며, 이는 데이터를 정렬된 상태로 저장하여 빠른 검색을 가능하게 합니다.

B-Tree 구조


B-Tree 인덱스 구조

  • 브랜치 노드: 각 노드는 여러 키와 자식 노드를 가지며, 데이터는 정렬된 형태로 저장됩니다.
  • 정렬된 데이터: 인덱스는 앞의 키를 기준으로 뒤의 키가 정렬되어 있습니다.
  • 빠른 검색: 로그 시간 복잡도로 데이터를 탐색할 수 있습니다.

카디널리티(Cardinality)란?

카디널리티는 데이터의 중복도를 나타내는 수치입니다.

  • 높은 카디널리티 (High Cardinality): 중복도가 낮은 데이터 (예: 주민등록번호, 핸드폰 번호).
  • 낮은 카디널리티 (Low Cardinality): 중복도가 높은 데이터 (예: 성별, 국가 코드).

카디널리티가 높을수록 인덱스의 효율성이 높아집니다.


인덱스 동작 방식

테이블에 인덱스를 설정할 때 고려해야 할 세 가지 경우가 있습니다.

1. 한 컬럼에 인덱스를 설정하는 경우

  • 데이터 중복도가 낮은 컬럼을 선택하여 인덱스를 설정합니다.
  • 높은 카디널리티를 가진 컬럼에 인덱스를 걸면 검색 성능이 향상됩니다.

2. 다중 컬럼에 각각 인덱스를 설정하는 경우

  • 여러 컬럼에 단일 컬럼 인덱스를 각각 설정합니다.
  • 쿼리 실행 시 MySQL은 Index Merge 최적화를 통해 인덱스 결과를 병합합니다.
  • 참고: MySQL Index Merge Optimization

3. 다중 컬럼 인덱스(Multiple-column Index)를 설정하는 경우

  • 여러 컬럼을 하나의 인덱스로 설정합니다.
  • 인덱스 설정 시 카디널리티가 높은 순서로 컬럼을 나열하는 것이 좋습니다.

카디널리티에 따른 인덱스 성능 비교

카디널리티 내림차순 (높은 → 낮은) 카디널리티 오름차순 (낮은 → 높은)
더 나은 성능 제공 상대적으로 낮은 성능 제공

3.1 다중 컬럼 인덱스 사용 조건

  • WHERE 절에서 =, >, <=, BETWEEN, IN 등의 연산자를 사용하는 경우 B-Tree 인덱스가 효율적으로 작동합니다.

    *"B-Tree 자료 구조는 WHERE 절에서 =, >, <=, BETWEEN, IN 등의 연산자에 대응하여 특정 값, 값의 집합 또는 값의 범위를 빠르게 찾을 수 있게 해줍니다."*

  • 조인 시 인덱스가 걸린 컬럼을 사용하면 성능이 향상됩니다.

    *"조인을 수행할 때 다른 테이블에서 행을 가져오기 위해 인덱스를 사용합니다. MySQL은 동일한 타입과 크기로 선언된 컬럼의 인덱스를 더 효율적으로 사용할 수 있습니다."*

  • 인덱스된 컬럼의 데이터 타입과 크기가 동일하면 더욱 효율적입니다.

    • 예를 들어, VARCHAR(10)CHAR(10)은 동일한 크기로 간주되지만, VARCHAR(10)CHAR(15)는 그렇지 않습니다.

인덱스된 컬럼의 크기와 성능

인덱스된 컬럼의 크기가 작을수록 성능이 향상됩니다. 아래는 데이터 타입별 크기와 성능을 비교한 예시입니다.

데이터 타입 크기 성능
BIGINT 8바이트 보통
UUID 16바이트 느림
VARCHAR(32) 최대 32바이트 빠름

풀 테이블 스캔을 피하는 방법

풀 테이블 스캔은 대량의 데이터를 처리할 때 성능 저하를 유발합니다. 이를 피하기 위해 다음을 고려해야 합니다.

  1. 적절한 인덱스 설정: 자주 조회되는 컬럼에 인덱스를 설정합니다.
  2. 쿼리 최적화: 불필요한 전체 테이블 검색을 피하도록 쿼리를 작성합니다.
  3. 통계 정보 최신화: ANALYZE TABLE 명령어를 사용하여 통계 정보를 업데이트합니다.

참고 자료:

반응형

+ Recent posts