소프트웨어 개발

'파티셔닝, 샤딩, 레플리케이션'에 대해 알아보자

hyunjuuun.dev 2024. 2. 11. 20:13

 

partitioning

파티셔닝이란 데이터베이스의 테이블을 더 작은 테이블로 나누는 것을 말한다.

파티셔닝의 종류는 크게 2가지로 나눌 수 있다.

vertical partitioning > column을 기준으로 테이블을 나눈다.

horizontal partitioning > row를 기준으로 테이블을 나눈다.

 

vertical partitioning (column을 기준으로 테이블 분리)

RDB를 설계하면서 데이터 중복을 제거하기 위해 사용하는 정규화 과정도 컬럼을 기준으로 테이블을 분리함으로 vertical partitioning이라고 할 수 있다.

 

정규화 이외에도 vertical partitioning은 다양한 상황에서 이용이 되는데 또 다른 예를 확인해 보자.

게시글에 대한 정보를 담고 있는 post 테이블이 있다.

post 테이블은 id, title, writer_id, create_date_time, comment_count, content 컬럼을 가진다고 해보자.

게시글 목록을 조회하는 페이지에서는 content 컬럼을 제외한 정보들이 필요하다.

select id, title, writer_id, create_date_time, commenc_count from post;

위와 같은 쿼리를 통해 데이터를 가져올 수 있을 것이다.

 

그런데 이렇게 실제 DB 테이블이 가지고 있는 정보들은 HDD나 SSD에서 읽어올 때 실제 조회를 원하는 컬럼 데이터만을 가져오는 것이 아니라 우선 테이블 내 모든 컬럼들의 데이터를 가져온 상태에서 원하는 컬럼 데이터만을 필터링하는 방식으로 조회하게 된다.

 

post 테이블을 보았을 때 content는 다른 컬럼들에 비해 사이즈가 큰 데이터이다. (작성자가 길게 썼다면 엄청 클 수도 있다.)

게시물 목록 페이지의 경우에는 실제 content 내용은 필요하지 않지만 HDD, SSD에서 모든 데이터를 읽어와서 메모리에 올린 다음 원하는 데이터만 필터링하여 조회하는 방식이기 때문에 데이터를 읽어오는 동안 I/O 작업으로 인한 부담이 생기게 된다.

이처럼 사이즈가 큰 데이터가 같은 테이블로 구성이 되어 있다면 I/O에 작업에 부담이 생기고 성능에 악영향을 줄 수 있다.

 

vertical partitioning을 통해 이 문제를 어떻게 해결할 수 있을까?

간단하게 post_content 테이블을 만들어주고 content 데이터만을 별도 테이블로 분리해 주면 된다.

이처럼 정규화가 되어 있는 테이블이더라도 vertical partitioning을 통해 애플리케이션 성능을 향상시킬 수 있다.

vertical partitioning은 이외에도

  • 민감한 정보에 대해 별도 테이블로 구성하는 용도로 사용할 수 있다.
  • 자주 사용되는 데이터와 자주 사용되지 않는 데이터를 별도 분리하여 구성하는 방식으로 사용할 수 있다.

 

horizontal partitioning (row를 기준으로 테이블 분리)

horizontal partitioning의 경우 테이블 구조에는 영향을 주지 않는다.

 

유튜브를 예로 들어보자.

user_id, channel_id, alarm, membership 컬럼으로 구성된 subscription 테이블이 있다.

해당 테이블에 최대로 쌓일 수 있는 row의 개수는 모든 사용자의 수 * 모든 채널의 수가 된다.

간단히 생각하더라도 테이블에 굉장히 많은 수의 row가 쌓일 수 있다는 것을 짐작할 수 있다.

여기서 고려해야 하는 것은 테이블에 쌓이는 데이터의 수가 많아질수록 인덱스의 크기도 커진다는 것이다.

인덱스란?

 

따라서 테이블에 읽기/쓰기 작업이 있을 때마다 인덱스에서 처리되는 시간도 계속해서 늘어나게 될 것이다.

이처럼 한 테이블에서도 굉장히 많은 데이터로 인해 성능 문제가 발생할 수 있을 것이다.

수평 파티셔닝을 통해 문제를 해결해 보자.

(수평 파티셔닝에도 다양한 방법이 있지만 그중에서 대표적인 hash 기반의 수평 파티셔닝을 사용했다고 가정)

  1. hash function을 통해서 user_id가 input으로 주어지면 output으로 0과 1 둘 중에 하나의 값을 주도록 한다.
  2. 기존 subscription 구조의 subscription_0 테이블과 subscription_1 테이블을 만든다.
  3. hash function을 통해 얻게 된 결괏값에 따라 subscription_0 또는 subscription_1에 저장한다.

기존에는 하나의 테이블에 저장되었다면 위와 같은 방식을 통해 2개의 테이블에 분할하여 데이터를 저장할 수 있게 되었다. 예시에서는 2개의 테이블로만 나눠 저장했지만 상황에 따라 더 많은 테이블로 나눠 저장하는 것도 당연히 가능하다.

이때 위 예시에서의 user_id와 같이 파티셔닝을 위한 기준이 되는 것을 'partition key'라고 부른다.

위와 같이 수평 파티셔닝을 진행했을 때 다음과 같은 요구사항이 있다고 해보자.

  1. 길동이가 구독한 모든 채널들의 id를 알고 싶다.
  2. id가 1인 채널을 구독한 모든 사용자의 id를 알고 싶다.

1번의 경우에는 길동이의 데이터가 있는 테이블로 가서 구독한 모든 채널의 조회하면 될 것이다.

반면 2번 요구사항을 위해서는 구분된 파티션에 대한 이점을 취할 수 없고 2개의 테이블 모두에서 데이터를 조회해야 한다.

따라서 가장 많이 사용될 케이스에 따라서 partition key를 정해야 한다. 또한 데이터가 균등하게 분배되도록 hash function을 잘 정의하는 것도 중요하다.

hash 기반 파티셔닝을 했을 때는 파티셔닝을 하여 운영을 한 뒤에 새로운 파티션을 추가하는 것은 어렵다는 것을 주의하여 초기에 파티션을 몇 개로 나눠서 사용할지 충분히 고려하여 결정해야 한다.

(기존 0,1 2개의 파티션으로 나눠 운영하다가 새롭게 2를 추가한다면 기존 0,1의 데이터들 중에서 hash function의 결괏값으로 2가 나오는 케이스를 모두 마이그레이션 해주어야 하기 때문)

 

sharding

샤딩이란 무엇일까?

각각의 파티션들을 서로 다른 DB에 저장하는 것을 말한다.

샤딩은 앞서 살펴본 horizontal partitioning과 비슷하게 동작한다.

하지만 샤딩을 하게 되면 나눠진 각 파티션이 독립된 DB 서버에 저장된다.

 

모든 파티션을 하나의 DB에 저장해두는 수평 파티셔닝에는 단점이 존재한다.

만약 사용자의 요청이 몰려오게 되면 어떤 파티션에 데이터가 담겨있는지에 상관없이 결국 하나의 DB의 CPU와 Memory를 사용하여 처리하게 된다. (한정된 하드웨어 자원)

반면 파티션을 각각 독립된 DB 서버에 저장해두면(샤딩) 찾고 있는 데이터에 따라 서로 다른 DB 서버에 요청이 전달될 것이다. (부하 분산)

트래픽이 많이 몰리는 테이블에 대해서는 샤딩을 통해 DB 서버의 부하를 낮출 수 있다.

샤딩의 경우 partition key를 shard key라고 부르며 각 파티션을 shard라고 부른다.

 

샤딩 시 고려해야 하는 문제들

샤딩은 DB 확장을 도와주는 훌륭한 기술이지만 샤딩 도입 시 고려해야 할 새로운 문제도 있다.

  • 데이터의 재샤딩
    • 데이터가 너무 많아져서 하나의 샤드로는 감당하기 어렵거나 샤드 간 데이터 분포가 균등하지 못하여 특정 샤드의 공간 소모가 다른 샤드에 비해 빠르게 진행되는 샤드 소진(shard exhaustion) 현상이 발생하면 샤드 키를 계산하는 함수를 변경하여 데이터를 재배치해야 한다. 안정 해시(consistent hashing)(추후 학습 후 정리 예정) 기법을 활용하면 이 문제를 해결 가능하다.
  • 유명 인사 문제 (핫스팟 키 문제)
    • 특정 샤드에 질의가 집중되어 서버 과부하가 걸리는 문제다. SNS 서비스에서 팔로워 수가 가장 많은 세 사람이 모두 같은 샤드에 저장되어 있다고 해보자. 해당 샤드에는 read 연산으로 인한 과부하가 발생할 것이다. 이 문제를 해결하기 위해서는 유명 인사 각각에 샤드 하나씩을 할당하거나 혹은 더 잘게 쪼개야 할 수도 있다.
  • 조인과 비정규화
    • 하나의 데이터베이스를 여러 샤드 서버로 분리하는 순간, 여러 샤드에 걸친 데이터를 조인하는 것은 어려워진다. 이를 해결하는 한 가지 방법은 데이터베이스를 비정규화하여 하나의 테이블에서 질의가 수행될 수 있게 하는 것이다.

 

replication

평상시에 요청을 처리하는 primary 서버가 있고 primary 서버를 복제해 둔 secondary 서버를 둔다. primary 서버의 변경 사항에 대해서는 secondary 서버에도 계속해서 동기화시켜둔다.

이를 통해 primary 서버에 장애가 발생했을 때 secondary 서버가 primary 서버를 대체하도록 하여 장애를 대응할 수 있도록 한다. (High availability 고가용성)

또한 장애 대응을 위한 용도뿐 아니라 서버로 들어오는 read 요청에 대한 분산 처리를 도와주기도 한다.

 

최종정리

partitioning: 목적에 따라 테이블을 작은 테이블들로 나누는 것

sharding: horizontal partitioning이면서 나누어진 테이블들을 별도 DB 서버에 저장하는 방식

replication: DB를 복제하여 여러 대의 DB 서버로 구성해 놓은 것