다음과 같은 Server - DB 아키텍처가 있다고 가정해보자.
Source / Replica 구조의 Replication 통해 가용성과 읽기/쓰기 작업의 부하 분산을 확보했다.
Replication에 대해 알아보고 싶다면 아래 포스팅을 참고하자.
2024.11.09 - [◼ CS 기초 지식/[데이터베이스]] - [MySQL] DB 레플리케이션에 대해 알아보자.
하지만 데이터는 지속적으로 축적될 것이고 10억건의 데이터가 쌓였을 때도 문제가 없을까?
인덱싱을 적절히 적용한다 하더라도 인덱스도 디스크 용량을 먹고 디스크 용량에도 제한이 있다.
Scale Up에도 한계가 있는 것이다.
이 문제를 해결할 수 있는 파티셔닝과 샤딩에 대해 한번 알아보자.
Partitioning (파티셔닝)
테이블을 더 작은 테이블들로 쪼개는 것을 말한다.
파티셔닝의 종류는 Vertical Partitioning, Horizontal Partitioning으로 2종류가 있다.
Vertical Partitioning (수직 파티셔닝)
'수직' 즉, 컬럼을 기준으로 테이블을 나누는 방식이다.
다음과 같이 View 화면이 그려진다고 가정해보자 (Tistory 블로그 글 관리 페이지)
그리고 다음과 같은 테이블을 구성했다고 가정해보자.
여기서 필요해 보이는 데이터는 게시글 ID, 제목, 카테고리, 작성자, 작성일자, 댓글 수로 총 6개로 보인다.
그럼 다음과 같이 쿼리를 구성할 수 있을 것이다.
SELECT 게시글_ID, 제목, 카테고리, 작성자, 작성일자, 댓글_수
FROM 게시글_테이블
WHERE ...
이 쿼리는 어떻게 동작할까?
과연 게시글_테이블에서 필요한 컬럼만 딱딱 읽어올까?
생각과 다르게 WHERE 조건에 맞는 데이터들을 게시글_테이블에서 조건에 맞는 전체 ROW를 디스크에서 전부 조회한다.
그 다음 메모리에 해당 데이터를 저장하고 SELECT 절의 컬럼들만 필터링해 반환하게 된다.
해당 View에서 필요없는 본문, 조회 수, 좋아요 수가 있다.
조회 수와 좋아요 수는 정수값으로 데이터 크기가 작지만 본문은 정말 긴 내용을 갖고 있어 데이터 크기가 아주 크다.
따라서 해당 View에 보여지지도 않는 큰 데이터 크기를 가진 본문에 대한 I/O가 불필요하게 발생하고 부담이 될 수 있다.
이 때 적용할 수 있는 것이 수직 파티셔닝이다.
컬럼을 기준으로 나눠 View에 필요한 데이터들만 담고 본문 관련된 데이터를 본문 테이블로 분리했다.
이제는 해당 View에 필요한 데이터들만 I/O작업을 하게 될 것이고 데이터 크기가 큰 본문으로 부터의 I/O 부담을 가지지 않게 된다.
이런식으로 이미 정규화가 잘 적용된 테이블이더라도 위와 같은 상황에서 성능을 위해 수직 파티셔닝을 적용할 수 있다.
Horizontal Partitioning (수평 파티셔닝)
'수평' 즉, 행을 기준으로 테이블을 나누는 방식이다.
모든 사용자들의 주문 정보를 저장하고 있는 테이블이 다음과 같이 구성되어있다고 가정해보자.
회원을 N이라하고, 주문 내역을 M이라고 해보자.
회원별로 주문은 여러번 할 수 있다.
대략 회원이 100만명이라고 가정하고 한 명당 1,000번의 주문을 했다고 가정하면
해당 테이블에는 1,000,000 * 1,000 = 10억건의 데이터가 쌓이게 된다.
여기서 끝일까? 시간이 지날수록 주문은 늘어날 것이고 데이터량은 점점 늘어나게 된다.
인덱스를 적절히 설정했다하더라도 인덱스의 크기도 커질 것이고 인덱스 처리 속도도 점점 늘어날 것이다.
이 문제를 해결할 수 있는 것이 바로 수평 파티셔닝이다.
(수평 파티셔닝에는 Range, List, Hash, Composite 파티셔닝이 있는데 이 관련 설명은 생략)
필자는 ‘월’을 기준으로 Range 파티셔닝을 적용해보았다.
주의할점
파티셔닝을 구분하는 ‘파티셔닝 키’를 적절하게 선택해
적절한 파티셔닝 키를 선택하여 분리해 특정 테이블에 데이터가 쏠리지 않도록 데이터가 균등하게 분산될 수 있도록 해야한다.
이제 특정 데이터를 조회하려면 다음과 같이 조회하면 된다.
SELECT *
FROM 주문_테이블
WHERE MONTH(주문일자) = 10
AND YEAR(주문일자) = 2024
ORDER BY 주문일자 DESC;
SELECT *
FROM 주문_테이블
WHERE 주문일자 >= '2024-07-01'
AND 주문일자 < '2024-12-01'
ORDER BY 주문일자 DESC;
잘 적용한걸까?
지금 적용한 파티셔닝에는 문제점이 있다.
바로 ‘파티션 프루닝’이 잘 동작하지 않는다는 것이다.
Partition Pruning (파티션 프루닝)이란?
조건절(WHERE)을 분석하여 불필요한 파티션을 제외하고 필요한 파티션만 검색하는 최적화 기법.
파티션 프루닝이 잘 동작하는 조건은 다음과 같다.
- 파티션 키에 대한 직접 비교
- 함수 사용하지 않기
- 범위 조건은 명확하게 지정
- 단일 파티션 접근이 가능하도록
- 가능한 한 단일 월 조회
- 필요한 경우 여러 쿼리로 분할
- 날짜 범위 지정 시
- 월의 시작일과 다음 월의 시작일 사용
- 파티션 경계와 일치하는 조건 사용
파티션 프루닝이 잘 동작하지 않으면 오히려 성능이 더 떨어질 수 있어 주의가 필요하다.
따라서 위의 경우 특정 년도의 월 별로 데이터를 조회하고 싶다면 파티셔닝을 년_월으로 파티셔닝하는게 적합할 수 있다.
파티션 프루닝이 효율적으로 동작할 수 있도록 가장 많이 사용될 패턴에 따라 파티션 키를 지정하는 것이 중요하다.
Sharding (샤딩)
파티셔닝은 하나의 DBMS에 있는 테이블들이 쪼개져 있는 형태이다.
하지만 수 많은 트래픽이 들어온다면 하나의 DBMS가 감당할 수가 없다.
샤딩은 파티션들을 서로 다른 DBMS에 저장하는 것과 같다.
즉, 부하를 분산 시키는 목적이다.
위 수평 파티셔닝에서 적용한 것을 샤딩으로 적용하면 다음과 같이 물리적으로 DBMS를 나눈다.
테이블이 아닌 DBMS를 추가하는 것이므로 월별로 나누기엔 너무 많은 DBMS가 추가되는데
이 또한 큰 비용이므로 분기별로 나누어주었다.
주의할점
샤딩 또한 수평 파티셔닝 처럼 적절한 ‘샤딩 키’를 선택해야 한다.
부하를 분산 시키는 것이 목적이기에
특정 DBMS에 데이터가 몰리지 않도록 적절히 분산시켜 각 DBMS 별로 라우팅해 부하 분산 시키는 것이 중요하다.
만약 위처럼 샤딩을 적용했는데 월별로 데이터가 너무 커진다면?
DBMS를 추가하여 각각의 데이터들을 마이그레이션하고 애플리케이션에서 적절한 DBMS를 라우팅하도록 수정해줄 수 있다.
즉, Scale Out으로 점진적인 확장성의 장점도 가질 수 있다.
참고자료
'◼ DB' 카테고리의 다른 글
[MySQL 8.0] 사용자(계정) 및 권한 정복하기 (0) | 2024.11.13 |
---|---|
[MySQL 8.0] 서버 설정, 시스템 변수 정복하기 (2) | 2024.11.12 |
[Redis] 캐싱(caching) 설계 전략에 대해 알아보자 (+TTL 설정 주의점) (0) | 2024.11.10 |
[MySQL] DB 레플리케이션에 대해 알아보자. (8) | 2024.11.09 |
MySQL에 나노초가 이상하게 저장되네? 발생할 수 있는 나노초 관련 이슈를 알아보자. (1) | 2024.10.20 |