Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions real-my-sql/haiseong/partitioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@

# 파티션

- 논리적으로는 하나의 테이블이지만 물리적으로는 여러 개의 테이블로 분리
- 대용량의 테이블을 물리적으로 여러 개의 소규모 테이블로 분산하는 목적으로 사용

## 13.1 개요

### 파티션을 사용하는 이유

#### 단일 INSERT와 단일 또는 범위 SELECT의 빠른 처리
- 인덱스가 커질수록 SELECT, INSERT, UPDATE 작업이 느려짐
- 한 데이불의 인덱스 크기가 물리적으로 MySQL이 사용 가능한 메모리 공간보다 크다면 심각하게 느림
- 파티션은 데이터와 인덱스를 조각화해서 물리적 메모리를 효율적으로 사용할 수 있게 만들어줌
- 워킹 셋(Working Set) : 활발하게 사용되는 데이터
- 워킹 셋과 그렇지 않은 부분으로 나눠서 파티션할 수 있다면 효과적

#### 데이터의 물리적인 저장소를 분리
- 데이터 파일이나 인덱스 파일이 파일 시스템에서 차지하는 공간이 크면 백업이나 관리 작업이 어려움
- MySQL은 데이불의 데이터나 인덱스를 파일 단위로 관리해서 더 치명적
- 파티션을 통해 파일의 크기를 조절, 파티션별 파일들이 저장될 위치나 디스크를 구분해서 지정함

로그는 단기간에 대량으로 누적되고 일정 기간이 지나면 쓸모가 없음
- 시간이 지나면 별도로 아카이빙, 백업한 후 삭제해버리는 것이 일반적
- 로그 테이불을 파티션 테이불로 관리한다면 불필요한 데이터 삭제 작업은 단순히 파티션을 추가하거나 삭제하는 것으로 해결

### MySQL 파티션의 내부 처리

```sql
mysql> CREATE TABLE tb_article (
article_id INT NOT NULL,
reg_date DATETIME NOT NULL,
...
PRIMARY KEY(article_id, reg_date)
) PARTITION BY RANGE( YEAR(reg_date) ) (
PARTITION p2009 VALUES LESS THAN(2010),
PARTITION p2010 VALUES LESS THAN(2011),
PARTITION p2011 VALUES LESS THAN(2012),
PARTITION p9999 VALUES LESS THAN MAXVALUE
);
```
- 등록 일자(reg_date)에서 연도는 파티션 키
- 해당 레코드가 어느 파티션에 저장될지를 결정

#### 파티션 테이블의 레코드 INSERT
- INSERT 되는 칼럼의 값 중에서 파티션 키인 reg_date 칼럼의 값을 이용해 파티션 표현식을 평가함
- 새로 INSERT되는 레코드를 위한 파티션이 결정되면 나머지 과정은 파티션되지 않은 일반 테이불과 동일 하게 처리

#### 파티션 테이블의 UPDATE
- 변경 대상 레코드가 어느 파티션에 저장돼 있는지 찾아야함
- WEHRE 조건에 파티션키 칼럼 조건 여부
- 있을때 : 레코드가 저장된 파티션에서 빠르게 대상 레코드를 검색 가능
- 명시되지 않았을때 : MySQL 서버는 변경 대상 레코드를 찾기 위해 데이불의 모든 파티션을 검색
- 파티션 키 변경 여부
- 파티션키 이외의 칼럼만 변경될 때 : 파티션이 적용되지 않은 일반 테이블과 마찬가지로 칼럼 값만 변경한다.
- 파티션 키 칼럼이 변경될 때 : 가존의 레코드가 저장된 파티션에서 해당 레코드를 삭제후 변경되는 파티션 키 칼럼의 표현식을 평가하고, 그 결과를 이용해 레코드를 이동시킬 새로운 파티션을 결정, 레코드를 새로 저장한다.

#### 파티션 테이블의 검색
- WHERE 절의 조건으로 검색해야할 파티션을 선택할 수 있는가?
- WHERE 절의 조건이 인덱스를 효율적으로 사용(인덱스 레인지 스캔) 할 수 있는가?

- 파티션 선택 가능 + 인덱스 효율적 사용
- 쿼리가 가장 효율적으로 처리
- 파티션의 개수와 관계없이 검색을 위해 꼭 필요한 파티션의 인덱스만 레인지 스캔
- 파티션 선택 불가 + 인덱스 효율적 사용 가능
- WHERE 조건에 일치하는 레코드가 저장된 파티션을 걸러낼 수 없어 모든 파티션을 대상으로 검색
- 각 파티션에 대해서는 인덱스 레인지 스캔을 사용할 수 있음
- 테이블에 존재하는 모든 파티션의 개수만큼 인덱스 레인지 스캔을 수행후 결과를 병합
- 파티션 선택 가능 + 인덱스 효율적 사용 불가
- 필요한 파티션만 읽으면 된다.
- 대상 파티션에 풀 테이블 스캔을 한다.
- 각 파티션의 레코드 건수가 많다면 상당히 느리게 처리
- 파티션 선택 불가 + 인덱스 효율적 서용 불가
- 모든 파티션을 검색
- 각 파티션에서 풀 테이블 스캔을 수행해야 한다.

#### 파티션 테이블의 인덱스 스캔과 정렬
- 모든 인덱스는 파티션 단위로 생성 (로컬 인덱스)
- 파티션과 관계없이 테이불 전체 단위로 글로벌하게 하나의 통합된 인덱스는 지원하지 않음
- p269 예제
- 파티션키 : YEAR(reg_date)
- reg_userid 칼럼의 값은 파티션의 순서대로 정렬돼 있지 않음
- 모든 파티션에 해당 쿼리를 날리고 나서 병합 후 소트버퍼에서 정렬해야하지 않을까?
- MySQL 서버는 여러 파티션에 대해 인덱스 스캔을 수행할 때 각 파티션으로부터 조건에 일치하는 레코드를 정렬된 순서대로 읽으면서 우선순위 큐(Priority Queue)에 임시로 저장후 우선순위 큐에서 다시 필요한 순서(인덱스의 정렬 순서)대로 데이터를 가져간다. (Merge & Sort 작업 = 우선순위 큐 처리 작업)

#### 파티션 프루닝
- 파티션 프루닝 : 옵티마이저에 특정 파티션을 읽지 않아도 된다고 판단되면 해당 파티션에는 전혀 접근하지 않음
- EXPLAIN 명령어를 통해 어떤 파티션만 조회하는지 확인 가능

## 주의사항

### 파티션의 제약 사항

1. 스토어드 루틴, UDF, 사용자 변수는 파티션 표현식에 사용할 수 없음
2. 파티션 표현식은 일반적으로 컬럼 자체나 MySQL 내장 함수를 사용할 수 있음, 일부 함수는 파티션 생성은 가능하지만 파티션 프루닝을 지원하지 않음
3. 프라이머리 키를 포함한 테이블의 모든 유니크 인덱스는 파티션 키 컬럼을 포함해함
4. 파티션된 테이블의 인덱스는 모두 로컬 인덱스, 개별 파티션의 인덱스를 변경하거나 추가할 수 없음
5. 동일 테이블에 속한 모든 파티션은 같은 스토리지 엔진만 사용가능
6. 최대 8192개의 파티션을 가질 수 있습니다 (서브 파티션 포함).
7. 파티션 생성 후 MySQL 서버의 sql_mode 시스템 변수 변경은 데이터 파티션의 일관성을 해칠 수 있음
8. 파티션 테이블에서는 외래키를 사용할 수 없음
9. 파티션 테이블에서는 전문 검색 인덱스 생성이나 전문 검색 쿼리를 사용할 수 없음
10. 공간 데이터를 저장하는 컬럼 타입(POINT, GEOMETRY 등)은 파티션 테이블에서 사용할 수 없음
11. 임시 테이블(Temporary table)은 파티션 기능을 사용할 수 없음


```sql
mysql > CREATE TABLE tb_article (
article_id INT NOT NULL AUTO_INCREMENT,
reg_date DATETIME NOT NULL,
...
PRIMARY KEY(article_id, reg_date)
) PARTITION BY RANGE( YEAR(reg_dat e) ) (
PARTITION p2009 VALUES LESS THAN (2010),
PARTITION p2010 VALUES LESS THAN (2011),
PARTITION p2011 VALUES LESS THAN (2012),
PARTITION p9999 VALUES LESS THAN MAXVALUE
)
```
- PRIMARY KEY(article_id, reg_date) : 파티션키가 프라이머리 키 일부로 참여해야하므로
- 파티션 표현식에 기본적인 산술연산자, ABS(), CEILING(), YEAR(), MONTH() 같은 내장 함수를 사용 가능
- 파티션 테이블을 설계할 때 파티션 프루닝 기능이 정상적으로 작동하는지 확인 해보기

### 파티션 사용 시 주의사항

#### 파티션과 유니크 키(프라이머리 키 포함)
- 파티션의 목적이 작업의 범위를 좁히는 것인데, 유니크 인덱스는 중복 레코드에 대한 체크 작업 때 문에 범위가 좁혀지지 않음
- 유니크 키에 대해 파티션 키가 제대로 설정됐는지 간단히 체크하려면 각 유니크 키에 대해 값이 주어졌을 때 해당 레코드가 어느 파티션에 저 장돼 있는지 계산할 수 있어야 한다.

#### 파티션과 open_files_limit 시스템 변수 설정
- MySQL 에서 테이블을 파일 단위로 관리하기 때문에 MySQL 서버에서 오픈한 파일의 개수가 상당히 많아질 수 있음
- open_files_limit 시스템 변수로 동시에 오픈할 수 있는 파일 개수 설정

## MySQL 파티션의 종류

### 레인지 파티션
- 파티션 키의 연속된 범위로 파티션을 정의
- 가장 일반적
- 다른 파티션 방법과는 달리 MAXVALUE라는 키워드 이용

#### 레인지 파티션의 용도
- 날짜를 기반으로 데이터가 누적되고 연도나 월, 또는 일 단위로 분석하고 삭제해야 할 때
- 범위 기반으로 데이터를 여러 파티션에 균등하게 나눌 수 있을 때
- 파티션키 위주로 검색이 자주 실행될 때
- 가장 자주 쓰이고 중요함
- 필요한 파티션만 접근하려는 파티션의 장점을 잘 살림

#### 레인지 파티션 테이블 생성
- PARTITIONBY RANGE키워드로 레인지 파티션을 정의
- PARTITIONBY RANGE뒤에 칼럼 또는 내장 함수를 이용해 파티션 키를 명시
- VALUESLESSTHAN으로 명시된 값보다 작은 값만 해당 파티션에 저장하게 설정 (LESSTHAN절에 명시된 값은 그 파티션에 포함되지 않음)
- VALUES LESS THAN MAXVALUE로 명시되지 않은 레코드를 저장할 파티션을 지정
- VALUES LESS THAN MAXVALUE가 정의되지 않으면 명시되지 않은 레코드가 INSERT될 때 에러가 발생하면서 "Table has no partition for value ..." 메시지가 표시
- 8.0에서는 기본으로 InnoDB 테이블로 생성

#### 레인지 파티션의 분리와 병합
- 단순 파티션 추가
- ALTER TABLE 명령
- ALTER TABLE ... REORGANIZE PARTITION 명령어
- 기존에 LESS THAN MAXVALUE에 있던 데이터들이 새로 추가되는 파티션과 중복될때 사용
- 레인지 파티션에서 일반적으로 LESS THAN MAXVALUE절을 사용하는 파티션은 추가하지 않고 미래에 사용될 파티션을 미리 2~3개 정도 더 만들어 두는 형태로 테이불을 생성
- LESS THAN MAXVALUE 가 있어도 빠르게 완료됨

#### 파티션 삭제
- DROP PARTITION 명령어
- 레인지 파티션을 사용하는 테이블에서는 가장 마지막 파티션만 새로 추가할 수 있고, 가장 오래된 파티션만 삭제 할 수 있음

#### 기존 파티션의 분리
- REORGANIZE PARTITION 명령
- 기존 파티션의 레코드를 새로운 파티션으로 복사해야 하기 때문에 기존 파티션의 레코드 건수에 따라 시간이 오래 걸릴 수도 있음
- INPLACE 알고리즘은 사용할 수 있지만 최소한 읽기 잠금(Shared Lock)이 필요

#### 기존 파티션의 병합
- REORGANIZE PARTITION 명령

### 리스트 파티션
- 리스트 파티션은 파티션 키 값 하나하나를 리스트로 나열해야 함

#### 리스트 파티션의 용도
- 파티션 키 값이 코드 값이나 카테고리와 같이 고정적일 때
- 키 값이 연속되지 않고 정렬 순서와 관계없이 파티션을 해야할 때
- 파티션 키 값을 기준으로 레코드의 건수가 균일하고 검색 조건에 파티션 키가 자주 사용될 때

#### 리스트 파티션 테이블 생성
- PARTITION BY LIST 키워드로 생성할 파티션이 리스트 파티션이라는 것을 명시
- PARTITION BY LIST 키워드 뒤에 파티션 키를 정의
- VALUES IN ( ... ) 으로 파티션별로 저장할 파티션 키 값의 목록을 나열 (NULL도 명시 가능)
- 레인지 파티션고는 달리, 나머지 모든 값을 저장하는 MAXVALUE 파티션은 정의할 수 없다.

### 해시 파티션
- 레인지 파티션이나 리스트 파티션으로 데이터를 균등하게 나누는 것이 어려울 때
- 테이블의 모든 레코드가 비슷한 사용빈도를 보이지만 테이블이 너무 커서 파티션을 적용해야 할때

#### 해시 파티션 테이블 생성
- PARTITION BY HASH키워드로 파티션 종류를 해시 파티션으로 지정
- PARTITION BY HASH키워드 뒤에 파티션 키를 명시
- 해시 파티션의 파티션 키 또는 파티션 표현식은 반드시 정수 타입의 값을 반환해야 함
- PARTITIONS n으로 몇 개의 파티션을 생성할 것인지 명시
- 파티션의 개수뿐만 아니라 각 파티션의 이름을 명시하려면 CREATE TABLE 명령과 같이 각 파티 션을 나열

#### 해시 파티션의 분리와 병합
- 해시 파티션의 분리와 병합은 리스트 파티션이나 레인지 파티션과는 달리 대상 테이블의 모든 파티션
에 저장된 레코드를 재분배하는 작업이 필요
- 파티션의 분리나 병합으로 인해 파티션의 개수가 변경된다는 것은 해시 합수의 알고리즘을 변경하는 것이므로 전체 파티션이 영향을 받는 것은 피할 수 없음

#### 해시 파티션 추가/병합
- 특정 파티션 키 값을 테이불의 파티션 개수로 MOD연산한 결팟값에 의해 각 레코드가 저 장될 파티션을 결정
- 해시 파티션은 데이블에 존재하는 파티션의 개수에 의해 파티션 알고리즘이 변경됨
- 새로운 파티션이 추가된다면 기존의 각 파티션에 저장된 모든 레코드가 재배치
- 해시 파티션에서 파티션을 추가하는 작업은 INPLACE알고리즘으로 실행된다고 하더라도 레코드 리빌드 작업 이 필요하며 테이불에 대한 읽기 잠금이 필요
- 병합은 COALESCE PARTITION 뒤에 명시한 숫자 값은 줄이고자 하는 파티션의 개수 명시

#### 주의사항
- 특정 파티션만 삭제하는 것은 불가능하다.
- 새로운 파티션을 추가하는 작업은 단순히 파티션만 추가하는 것이 아니라 기촌 모든 데이터의 재배치 작업이 필요

### 키 파티션
- 키 파티션은 해시 파티션과 사용법과 특성이 거의 같음
- 키 파티션에서는 정수 타입이나 정숫값을 반환하는 표현식뿐만 아니라 대부분의 데이터 타입에 대해 파티션 키를 적용할 수 있다.
- MySQL 서버는 선정된 파티션 키의 값을 MD5() 함수를 이용해 해시 값을 계산
- 그값을 MOD 연산해서 데이터를각파티션에 분배

#### 키 파티션의 생성
- PARTITION BY KEY 키워드로 키 파티션을 정의한다.
- PARTITION BY KEY 키워드 뒤에 파티션 키 칼럼을 명시한다.
- PARTITIONBY KEY() 에 아무 칼럼도 명시하지 않으면 MySOL서버가 자동으로 프라이머리 키나 유니크 키의 모든 칼럼을 파티션 키로 선택
- 테이블에 프라이머리 키가 있다면 프라이머리 키의 모든 칼럼으로, 프라이머리 키가 없는 경우에는 유니크 인덱스의 모든 칼럼으로 파티션 키를 구성
- 프라이머리 키나 유니크 키를 구성하는 칼럼 중에서 일부만 파티션 키로 명시하는 것도 가능하다.
- PARTITIONS 키워드로 생성할 파티션 개수를 지정한다.

#### 키 파티션의 주의사항 및 특이사항
- 키 파티션은 MySQL 서버가 내부적으로 MD5() 함수를 이용해 파티션하기 때문에 파티션 키가 반드시 정수 타입이
아니어도 됨 해시 파티션으로 파티션이 어렵다면 키 파티션 적용을 고려
- 프라이머리 키나 유니크 키를 구성하는 칼럼 중 일부만으로도 파티션 가능
- 유니크 키를 파티션 키로 사용할 때 해당 유니크 키는 반드시 NOT NULL 이어야 함
- 해시 파티션에 비해 파티션간의 레코드를 더 균등하게 분할할 수 있기 때문에 키파티션이 더 효율적

### 리니어 해시 파티션 / 리니어 키 파티션
해시 파티션이나 키 파티션은 새로운 파티션을 추가 하거나 파티션을 통합해서 개수를 줄일 때 대상 파티션만이 아니라 테이불의 전체 파티션에 저장된 레코드의 재분배 작업이 발생

#### 리니어 해시 파티션/리니어 키 파티션의 추가
- 이러한 단점을 최소화하기 위해 리니어 해시 파티션 / 리니어 키 파티션 알고리즘이 고안
- 리니어 해시 파티션 / 리니어 키 파티션은 각 레코드 분배를 위해 Power―of―two(2의 승수) 알고리즘을 이용
- 파티션의 추가나 통합 시 다른 파티션에 미치는 영향을 최소화

#### 리니어 해시 파티션 / 리니어 키 파티션과 관련된 주의사항
- 각 파티션이 가지는 레코드의 건수는 일반 해시 파티션이나 키 파티션보다는 덜 균등
- 새로운 파티션을 추가하거나 삭제해야할 요건이 많다면 리니어 해시 파 티션 또는 리니어 키 파티션을 적용하는 것이 좋음

### 파티션 테이블의 쿼리 성능
- 파티션 테이블에 쿼리가 실행될 때 테이불의 모든 파티션을 읽을지 아니면 일부 파티션만 읽을지는 성능에 아주 큰 영향
- 얼마나 많은 파티션을 파티션 프루닝 할 수 있는지가 관권