diff --git a/real-my-sql/haiseong/partitioning.md b/real-my-sql/haiseong/partitioning.md new file mode 100644 index 0000000..366ffd7 --- /dev/null +++ b/real-my-sql/haiseong/partitioning.md @@ -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의 승수) 알고리즘을 이용 + - 파티션의 추가나 통합 시 다른 파티션에 미치는 영향을 최소화 + +#### 리니어 해시 파티션 / 리니어 키 파티션과 관련된 주의사항 +- 각 파티션이 가지는 레코드의 건수는 일반 해시 파티션이나 키 파티션보다는 덜 균등 +- 새로운 파티션을 추가하거나 삭제해야할 요건이 많다면 리니어 해시 파 티션 또는 리니어 키 파티션을 적용하는 것이 좋음 + +### 파티션 테이블의 쿼리 성능 +- 파티션 테이블에 쿼리가 실행될 때 테이불의 모든 파티션을 읽을지 아니면 일부 파티션만 읽을지는 성능에 아주 큰 영향 +- 얼마나 많은 파티션을 파티션 프루닝 할 수 있는지가 관권 +