
table을 파티션 단위로 나누어 관리하는 파티셔닝 기법이 나타나게 됩니다.table을 파티션이라는 작은 단위로 분리하는것을 말합니다.장점
특정DML 쿼리성능 향상
대용량 Data WRITE환경
Full Scan에서 범위축소
OLTP(온라인트랜랙션) 에서 INSERT 작업을 파티션단위로 분산시켜 경합감소
회손가능성감소
파티션별 독립적 백업, 복구 가능
table의 partition 단위로 Disk I/O을 분산하여 경합을 줄이기 때문에 UPDATE 성능을 향상
관리가 용의
단점
수평 파티셔닝
하나의 테이블의 각 행을 다른 테이블에 분산시키는 것입니다.
샤딩과 동일한 개념
가장 일반적인 파티셔닝
KEY를 기반으로 분산저장
장점
데이터 개수가 작아지고 index개수도 작아지기 때문에 성능향상
데이터 개수 기준 파티셔닝
단점
서버 연결과정 많아짐
데이터를 찾는 과정이 기존보다 복잡하기 때문에 latency증가
수직 파티셔닝
테이블의 일부 열을 빼내는 형태로 분할합니다.
관계형 DB에서 3정규화와 같은 개념으로 접근하면 이해하기 쉬움
하지만 수직 파티셔닝은 이미 정규화된 데이터를 분리하는 과정
예) 유저의 주소를 보안이슈등으로 CustomerId를 참조하여 다른 테이블 분리
장점
자주 사용하는 컬럼 등을 분리시켜 성능을 향상시킬 수 있음
I/O 측면에서 봤을 때 필요한 컬럼만 올리면 훨씬 많은 수의 ROW를 메모리에 올릴 수 있으니 성능상의 이점이 있음
* 데이터 타입끼리 파티셔닝하게되면 저장시 데이터 압축률 높일 수 있음
데이터베이스 관리 시스템은 분할에 대해 각종 기준(분할 기법)을 제공하고 있다. 분할은 ‘분할 키(partitioning key)’를 사용합니다.
범위 분할 (range partitioning)
분할 키 값이 범위 내에 있는지 여부로 구분한다.
예를 들어, 우편 번호를 분할 키로 수평 분할하는 경우이다.
목록 분할 (list partitioning)
값 목록에 파티션을 할당 분할 키 값을 그 목록에 비추어 파티션을 선택한다.
예를 들어, Country 라는 컬럼의 값이 Iceland , Norway , Sweden , Finland , Denmark 중 하나에 있는 행을 빼낼 때 북유럽 국가 파티션을 구축 할 수 있다.
해시 분할 (hash partitioning)
해시 함수의 값에 따라 파티션에 포함할지 여부를 결정한다.
예를 들어, 4개의 파티션으로 분할하는 경우 해시 함수는 0-3의 정수를 돌려준다.
합성 분할 (composite partitioning)
상기 기술을 결합하는 것을 의미하며, 예를 들면 먼저 범위 분할하고, 다음에 해시 분할 같은 것을 생각할 수 있다.
컨시스턴트 해시법은 해시 분할 및 목록 분할의 합성으로 간주 될 수 있고 키 공간을 해시 축소함으로써 일람할 수 있게 한다.
범위를 기반으로 파티션을 나누는 형식이다.
Range Partition은 날씨 기반 데이터가 누적되고 날짜에 따라 분석 삭제할 경우, 범위 기반으로 데이터를 여러 파티션에 균등하게 나눌 수 있는 경우, 대량의 과거 데이터 삭제 같은 경우에 사용한다.
Range Partition에서 null은 어떤 값 보다 작은 값으로 취급되기 때문에 컬럼에 null인 데이터가 insert 된다면 가장 작은 값을 저장하는 파티션에 저장된다.
CREATE TABLE test (
id INT NOT NULL,
lname VARCHAR(30),
datt DATE NOT NULL DEFAULT '2000-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
store_id INT NOT NULL
) PARTITION BY RANGE (YEAR(datt)) (
PARTITION p0 VALUES LESS THAN (2020) ,
PARTITION p1 VALUES LESS THAN (2021) ,
PARTITION p2 VALUES LESS THAN (2022) ,
PARTITION p3 VALUES LESS THAN MAXVALUE
);
연도를 범위를 기준으로 파티션을 생성한 것이다.
List Partition은 RangePartition과 비슷하고,코드나 카테고리 등 특정 값을 기반으로 파티션을 나눈다.
List Partition은 파티션 키 값이 코드 값이나 카테고리와 같이 고정 값일 경우에 사용하고 파티션 키 값을 기준으로 레코드 건수가 균일하고 검색 조건에 파티션 키가 자주 사용되는 경우에 사용한다.
List는 Range와 다르게 null을 명시할 수 있지만, MAXVALUE는 지정할 수 없다.
CREATE TABLE test (
id INT NOT NULL,
name VARCHAR(30),
datt DATE NOT NULL DEFAULT '2000-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
) PARTITION BY LIST (job_code) (
PARTITION p0 VALUES IN (2) ,
PARTITION p1 VALUES IN (1,9) ,
PARTITION p2 VALUES IN (3,6,7) ,
PARTITION p3 VALUES IN (4,5,8, NULL)
);
Hash Partition은 Hash 함수에 의해 레코드가 저장될 파티션을 결정하는 방식이다.
Hash Partition은 테이블의 모든 레코드가 비슷한 사용빈도를 보이지만 너무 커서 파티션이 필요한 경우 사용된다.
CREATE TABLE test (
id INT NOT NULL,
name VARCHAR(30),
datt DATE NOT NULL DEFAULT '2000-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
) PARTITION BY HASH (id)
PARTITIONS 4;
파티션 키 값을 MD5 함수를 이용하여 계산한 후 MOD 연산하여 파티션에 분배하는 방식. 정수 타입 외에 다른 데이터 타입들을 파티션 키로 사용할 수 있다는 것이 해시 파티션과의 차이점이다.
CREATE TABLE employees(
id INT NOT NULL,
...
) PARTITION BY KEY() PARTITIONS 4;
연월 별 데이터를 나누고 싶다면 아래와 같이 정의할 수 있습니다.
mysql> CREATE TABLE users (
user_id INT NOT NULL,
reg_at DATETIME NOT NULL,
...
PRIMARY KEY(user_id, reg_at)
) PARTITION BY RANGE (TO_DAYS(reg_at)) (
PARTITION P_202206 VALUES LESS THAN (TO_DAYS('2022-07-01')),
PARTITION P_202207 VALUES LESS THAN (TO_DAYS('2022-08-01')),
PARTITION P_202208 VALUES LESS THAN (TO_DAYS('2022-09-01')),
PARTITION P_maxvalue VALUES LESS THAN MAXVALUE
);
PRIMARY KEY(user_id, reg_at)여기서 파티션을 할 컬럼은 PRIMARY KEY로 등록되어야 합니다.
참고로, 파티션은 동일한 테이블의 형식으로 나눌 뿐, 파티션 별 다른 형태를 갖지 않습니다.
가령 각각 다른 인덱스를 생성하는 등의 형태는 지원하지 않습니다.
users 테이블은 reg_at 칼럼에서 TO_DAYS( ) 라는 MySQL 내장 함수를 이용해 날짜만 추출하고, 그 날짜를 이용해 테이블을 연도 범위별로 파티션합니다.
(mysql 내장함수들)
ABS(), CEILING(), EXTRACT(), FLOOR(), MOD(),
DATEDIFF(), DAY(), DAYOFMONTH(), DAYOFWEEK(), DAYOFYEAR(), HOUR(), MICROSECOND(), MINUTE(),
MONTH(), QUARTER(), SECOND(), TIME_TO_SEC(), TO_DAYS(), TO_SECONDS(), UNIX_TIMESTAMP(),
WEEKDAY(), YEAR(), YEARWEEK()
1. PARTITION BY RANGE ( ... )
RANGE 기반의 파티셔닝을 한다는 의미입니다.
RANGE 이외에도 LIST, HASH, KEY Partition 이 존재합니다.
2. TO_DAYS(reg_at)
Partition Key로 사용할 컬럼을 지정합니다.
예시에서는 reg_at 컬럼을 지정했는데, 하나 이상의 컬럼이 Partition Key가 될 수 있습니다.
TO_DAYS는 날짜 데이터를 정수형으로 변환시키기 위한 MySQL 내장함수입니다.
내장 함수는 지난 포스팅의 Function 섹션을 참고해주세요.
3. PARTITION VALUES LESS THAN (TO_DAYS('2022-07-01'))
파티셔닝을 진행할 기준 값들을 정의합니다.
정의한 값을 기준으로 데이터를 삽입하고 조회하는 검색 시점의 기준 값이 됩니다.
이 기준 값에 따라 데이터가 저장되기 때문에 데이터가 모든 파티션에 골고루 퍼지도록 잘 정의해야 합니다.
4. PARTITION P_maxvalue VALUES LESS THAN MAXVALUE
마지막 구문인 MAXVALUE 는 만약 정의한 날짜 값 그 이외의 값이 삽입될 때를 대비합니다.
위의 예시에서 2022년 10월의 데이터, 혹은 그 이후의 들어온다면 처리되지 못하니, maxvalue 파티션에 추가하라는 의미입니다.
-MAXVALUE <= intval <= MAXVALUE
위와 같은 범위를 갖습니다.
실제 데이터들은 파일로 관리되며, my.conf 내에 정의된 데이터 저장 위치에서 확인이 가능하죠.
파티션 데이터는 <table_name>#P#<partition_name>.ibd 형식으로 나눠집니다.
파티션 설명 중 논리적으로는 하나의 테이블과 같은데 물리적으로 분리된 구조라는 의미라는것이 이런 의미 입니다.
Alter Exist Table
혹은 기존의 테이블을 파티셔닝할 수 있습니다.
형식은 거의 동일합니다.
CREATE TABLE mirrorline.users_after (
`user_id` INT NOT NULL AUTO_INCREMENT,
`email` VARCHAR(100) NOT NULL,
`name` VARCHAR(15) NOT NULL,
`age` INT NOT NULL,
`reg_at` DATETIME NOT NULL,
PRIMARY KEY (`user_id`, `reg_at`)
);
기본 테이블이 이렇게 존재할때
ALTER TABLE users_after PARTITION BY RANGE (TO_DAYS(reg_at)) (
PARTITION P_202206 VALUES LESS THAN (TO_DAYS('2022-07-01')),
PARTITION P_202207 VALUES LESS THAN (TO_DAYS('2022-08-01')),
PARTITION P_202208 VALUES LESS THAN (TO_DAYS('2022-09-01')),
PARTITION P_maxvalue VALUES LESS THAN MAXVALUE
);
파티션을 추가합니다. 참고로, 데이터가 존재할 때에도 파티션으로 나눌 수 있습니다. 다만, 데이터가 많을 경우에 서비스되고 있는 테이블을 나누려면 부하가 크게 갈테니 주의하세요.
Partition 확인
SELECT PARTITION_NAME,TABLE_ROWS
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 'users';
예시
INSERT INTO users(email, name, age, reg_at)
VALUES('gngsn@gmail.com', 'gngsn', 25, '2022-12-23 12:02:28');
이런 값을 추가하면
p_maxvalue쪽으로 자동으로 파티셔닝되게 됩니다.
DROP
ALTER TABLE users DROP PARTITION P_202207;
파티션을 제거하고 싶다면 아래와 같은 쿼리를 입력하면 됩니다.
그럼, 기존 P_202206 파티션에 저장된 데이터들은 파티션과 함께 삭제됩니다.
SELECT
SELECT * FROM users PARTITION (P_202207);
SELECT * FROM users PARTITION (P_maxvalue);
이런식으로 파티션 하나만 조회할수도있습니다.
EXPLAN으로 확인해보면 특정 파티션만 검색한것을 확인할 수 있습니다.
병합
ALTER TABLE test
REORGANIZE PARTITION p2,p3 INTO (
PARTITION p23 VALUES LESS THAN (2020)
);
성능고려주의점
파티션을 사용하면 성능이 오히려 떨어질 수 있습니다.
데이터 검색을 먼저 구분해보자면, WHERE 절의 조건으로 아래와 같은 사항을 따져볼 수 있습니다.
검색할 파티션을 찾을 수 있는지
인덱스를 효율적으로 사용할 수 있는지 (Index Range Scan)
파티션 선택 O, 인텍스 효율 O
* 이때는 파티션의 개수와 관계없이 검색을 위해 꼭 필요한 파티션의 Index Range Scan하는 경우를 의미합니다. 두 조건이 모두 가능할 때 쿼리가 가장 효율적으로 처리될 수 있습니다.
파티션 선택 X, 인덱스 효율 O
이 경우 우선 테이블의 모든 파티션을 대상으로 검색해야 하지만, 각 파티션에 Index Range Scan을 사용할 수 있습니다.
최종적으로 테이블에 존재하는 모든 파티션의 개수만큼 Index Range Scan 검색 하게 됩니다.
* 이 작업은 파티션 개수만큼의 테이블에 대해 인덱스 레인지 스캔을 한 다음, 결과를 병합해서 가져오는 것과 같습니다.
파티션 선택 O, 인덱스 효율 X
* 인덱스는 이용할 수 없어서 대상 파티션에 대해 Full Table Scan 하기 때문에, 각 파티션의 레코드 건수가 많다면 상당히 느리게 처리됩니다.
파티션 선택 X, 인텍스 효율 X
* 테이블의 모든 파티션을 검색해야 하고 각 파티션에서도 Full Table Scan을 수행해야 합니다. 최악의 경우 입니다.
파티션 선택 O, 인덱스 효율 X, 파티션 선택 X, 인텍스 효율 X의 경우 파티션을 지양하는것이 좋습니다.
INSERT
UPDATE
데이터를 변경할 때는 파티션 키를 변경하는지 아닌지로 구분해서 설명할 수 있습니다.
Index
모든 인덱스는 파티션 단위로 생성됩니다.
MySQL의 파티션 테이블에서 인덱스는 전부 로컬 인덱스로, 테이블 단위의 글로벌한 인덱스는 지원하지 않습니다.
파티션되지 않은 테이블에서는 인덱스를 순서대로 읽으면 그 칼럼으로 정렬된 결과를 바로 얻을 수 있지만, 파티션된 테이블은 인덱스가 분리되어 있기 때문에 다르게 동작합니다.
일반 테이블의 인덱스 스캔처럼 결과를 바로 반환하는 것이 아니라, 여러 파티션에 대해 인덱스 스캔을 수행할 때 각 파티션으로부터
조건에 일치하는 레코드를 정렬된 순서대로 읽으면서 우선순위 큐(Priority Queue)에 임시로 저장하여 가져옵니다.
각 파티션 에서 읽은 데이터가 이미 정렬되어 있기 파티션에 접근한 후 그 순서대로만 가져오며, 내부적으로 큐 처리를 합니다.
최적화 단계에서 필요한 파티션만 골라내고 불필요한 것 들은 실행 계획에서 배제, 옵티마이저가 다수의 파티션 중 일부만 읽어도 된다고 판단되면 불필요한 파티션에는 전혀 접근하지 않습니다.
mysql> EXPLAIN SELECT * FROM users WHERE reg_at > '20220801' AND reg_at > '20220801';
+----+------------+------------+-------+---------+---------+------+--------------------------+
| id | table | partitions | type | key | key_len | rows | Extra |
+----+------------+------------+-------+---------+---------+------+--------------------------+
| 1 | tb_article | P_202208 | index | PRIMARY | 9 | 1 | Using where; Using index |
+----+------------+------------+-------+---------+---------+------+--------------------------+
https://gmlwjd9405.github.io/2018/09/24/db-partitioning.html
https://gngsn.tistory.com/203
https://gngsn.tistory.com/204?category=851218