파티셔닝

파티셔닝은 데이터베이스의 확장 방식 중 하나로, 데이터셋이 아주 크거나 복제만으로 처리하기 어려울 정도로 쿼리량이 높을 경우 사용합니다. 다른 말로는 샤딩 이라고도 합니다.

파티셔닝을 하는 목적은 확장성 을 확보하기 위해서입니다. 앞으로 다양한 파티셔닝 방식과, 파티션을 관리하는 방식들에서 알아보게 될텐데, “확장성을 지키기 위해서 어떤 고민을 했는가?” 를 마음에 두고 읽어보시면 좋을 것 같습니다.

파티셔닝 기준 세우기

파티셔닝을 통해 확장성이라는 이점을 얻으려면 데이터와 쿼리 부하가 각 노드들 사이에 고르게 분산되어야 합니다. 부하 분산이 고르게 될 경우 읽기/쓰기 요청에 대해 노드의 개수만큼 처리량이 늘어납니다.

하지만 부하가 고르게 분산되지 않아 특정 파티션에 데이터가 너무 많거나, 쿼리를 다른 파티션에 비해 많이 받는다면 데이터가 쏠렸다고 합니다. 데이터가 쏠린 부분을 핫 스팟 이라고 하며, 이 경우 노드 자원이 효율적으로 분배되지 않았다고 할 수 있습니다.

그래서 데이터셋을 어떻게 나눌 것인지에 대해서 파티셔닝 기준을 세우는 일이 매우 중요합니다.

무작위 파티셔닝

핫스팟을 피하는 가장 간단한 방법은 무작위로 레코드를 저장할 파티션을 선택하는 방식입니다. 하지만 이 경우 특정 레코드가 어떤 파티션에 존재하는지 알 수 없기 때문에 읽기 요청을 모든 파티션에 보내야 한다는 큰 단점이 있습니다. 이 경우 불필요한 쿼리가 늘어나므로 파티셔닝의 장점인 확장성을 살렸다고 보기 힘듦니다.

따라서 보통 이 방식이 아니라 다른 방식을 사용합니다.

키 범위 기준 파티셔닝

키 범위 기준 파티셔닝의 가장 쉬운 예시로는 백과사전을 생각하면 됩니다. 백과사전이 이름 기준으로 알파벳 혹은 가나다 순으로 정리된것처럼, 각 파티션에 연속된 범위 의 키를 할당하는 방식입니다.

이 경우 백과사전이 A, B, … , T-Z 로 나눠지는 것처럼 키 범위를 동일하게 할 필요는 없고 데이터를 고르게 분산하는 방식으로 경계를 설정하면 됩니다. 경계 설정은 자동으로 할수도 있고 수동으로 할수도 있습니다.

이 방식의 장점으로는 각 파티션 안에 키를 정렬해 두면 키 자체를 색인으로 사용하여 특정 범위에 대한 쿼리를 처리하는 속도가 매우 빨라질 수 있습니다.

하지만 일자별로 파티셔닝을 했을 때 특정 날짜에 대한 쿼리가 많은 접근 패턴의 경우 ( 당일에 발생한 이벤트 기록 ) 부하가 분산되지 않고, 쏠림과 핫스팟 문제가 발생할 수 있습니다.

해시값 기준 파티셔닝

특정 키에 대해서 정해진 특정 값을 반환하는 해시 함수를 이용하여 키에 대한 해시값을 이용한 파티셔닝을 할 수 있습니다. 이렇게 할 경우 위에서의 핫스팟 문제를 피할 수 있다는 장점이 있습니다.

하지만 키 범위 파티셔닝에서의 효율적인 범위 쿼리라는 장점을 포기해야 합니다. (키가 가까이 있다고 해시 함수가 같은 파티션으로 배정하지 않기 때문)

따라서 해시값 기준 파티셔닝과 키 범위 기준 파티셔닝을 동시에 사용하는 복합 기본키 전략을 사용하는 DB도 존재합니다(카산드라)

하지만 이렇게 파티셔닝을 해도 핫스팟과 쏠림 현상에서 완벽히 자유롭지는 않습니다. 예를 들어 트위터나 인스타그램 처럼 특정 유명인의 계정, 게시물에 대한 요청이 극단적으로 많은 경우 ( 같은 키에 대한 요청이 많은 경우 ) 에서는 어쩔 수 없이 모든 요청이 특정 파티션에 쏠리게 됩니다.

이런 경우에는 DB에서 부하 보정이 불가능하기 때문에 어플리케이션 단에서 처리를 한다던가 하는 로직이 필요합니다.

파티셔닝과 인덱싱

데이터베이스의 성능을 높이기 위해서 인덱싱도 매우 중요합니다. DB를 파티셔닝하게 되면 인덱싱 데이터도 나눠지게 되는데, 인덱스를 파티셔닝하는 방법은 크게 2가지로 나뉩니다.

지역 인덱싱 (문서 기준 파티셔닝)

이 방법은 각 파티션별로 파티션 내에 저장되어 있는 데이터에 대해서 인덱싱을 하는 방식입니다. 이 경우 각 파티션마다 인덱스가 독립적이기 때문에 지역 색인 이라고도 합니다.

각 파티션이 독립적이므로 쓰기 작업 시 색인 관리가 간편하지만, 읽기 쿼리를 보낼 때 모든 파티션에 대해서 쿼리를 보내서 색인을 이용해야 한다는 단점이 있습니다.

전역 인덱싱(용어 기준 파티셔닝)

전역 인덱싱의 경우 각 파티션마다 색인을 따로 생성하는 대신 모든 파티션의 데이터에 대한 색인을 만들고, 이 색인을 파티션들에 나눠놓는 방식으로 구현할 수 있습니다.

이 경우 특정 레코드에 대한 색인이 해당 파티션이 아니라 다른 파티션에 존재할 수 있습니다. 이를 용어 기준 파티셔닝 이라고 합니다.

이 방법의 장점으로는 읽을 때 특정 파티션 하나에 대해서만 쿼리해도 전체 색인을 얻을 수 있기 때문에 읽기가 효율적입니다. 하지만 쓰기 작업 시 전역의 인덱스를 업데이트해 줘야 하므로 쓰기가 느려질 수 있다는 단점이 있습니다.

파티션 리밸런싱

파티셔닝을 해 두더라도 서비스의 성장에 따라 쿼리량이 늘어날 수도 있고, 파티션에 대한 부하가 너무 커질수도 있습니다. 이럴 경우에는 보통 노드를 추가하게 되는데, 노드가 추가되었을 때 기존 노드와의 균형을 이뤄야 하기 때문에 기존 노드에 있던 데이터를 새로운 노드로 옮기게 됩니다.

이러한 과정을 재균형화(Reblancing) 이라고 부릅니다. 리밸런싱을 할 때 중요하게 생각해야 하는 점들은 다음과 같습니다.

  • 리밸런싱의 결과로 부하가 모든 노드에 균등하게 분배되어야 한다.

  • 리밸런싱 중에도 시스템에 읽기/쓰기 요청이 정상적으로 작동해야 한다.

    • DB는 항상 운영중이어야 하는 경우가 많기 때문

  • 리밸런싱은 빨리 끝날수록 좋고, 디스크 및 네트워크 부하를 최소화해야 한다.

    • 리밸런싱에 네트워크 자원을 많이 사용하면 쿼리 처리에 사용할 수 있는 부하가 줄어들기 때문.

이러한 점을 바탕으로 다양한 리밸런싱 전략을 살펴 보겠습니다.

Mod N 연산

간단하게 모듈러 연산을 이용해서 데이터를 리밸런싱하면 되지 않을까 할수도 있지만, 그럴 경우 옮겨야 하는 데이터가 너무 많이 생기므로 절대 사용하면 안되는 방법입니다.

파티션 개수 고정

애초에 파티션을 노드 수보다 많이 만들고, 노드가 늘어나면 파티션만 통째로 옮기는 방법입니다.

이 경우 파티션 개수를 초기에 잘 정해야 하는데, 처음 설정한 파티션의 개수보다 노드의 수가 늘어날 수 없기 때문에 너무 낮은 값으로 선택하면 안됩니다. 하지만 너무 높은 값으로 설정하면 파티션 정보에 대한 메타데이터를 관리하는 오버헤드가 커지므로 적절한 값을 선택하는 것이 중요합니다

데이터셋의 크기 변동이 심할 경우에는 적절한 파티션 개수를 예측하기 어렵기 때문에 사용하기 어려운 방법입니다.

동적 파티셔닝

키 범위 파티셔닝을 사용하는 경우, 해당 키 범위에 대한 부하가 너무 늘어났을 때 파티션을 키 기준으로 쪼갤 수 있습니다.

사실 수동으로 처음에 파티션을 정할 경우 부하 예측이 잘못될 수 있기 때문에 키 범위 파티셔닝을 사용할 경우 파티션을 동적으로 나누는 경우가 있습니다.

단점으로는 초기에는 노드가 여러개여도 파티션을 쪼갤 부하에 대한 정보가 없으므로 파티션 하나로 사용해야 한다는 단점이 있습니다. 이를 극복하기 위해서 초기에 파티션 범위를 미리 정해놓을 수도 있습니다.(pre-splitting)

노드 비례 파티셔닝

파티션의 개수가 노드 개수에 비례하도록 하는 방식입니다. 이 경우 개별 파티션의 크기를 안정적으로 유지할 수 있다는 장점이 있습니다.

새 노드가 추가되었을 때 기존 파티션을 무작위로 선택하여 분할한 뒤 분할된 것의 반을 새 노드에 할당하는 방식으로 리밸런싱이 이뤄집니다. 따라서 해시 값 기반 파티셔닝 방식을 사용하는 것이 일반적입니다.

이렇게 다양한 리밸런싱 전략이 있는데, 리밸런싱은 자동으로 처리하는 것이 좋을까요? 아니면 수동으로 관리자의 승인 하에 처리하는 것이 좋을까요?

자동으로 처리하면 유지보수에 필요한 인력이 줄어들기 때문에 편리하지만, 네트워크 과부하 등의 문제와 자동 리밸런싱이 겹칠 경우 오히려 더 큰 장애를 유발할 수 있기 때문에 사람이 중간에 개입하는 것도 괜찮은 방법일 수 있습니다.

결국 이번 파트의 결론도 복제 파트와 비슷하다고 볼 수 있습니다. 어떤 방식도 다른 방식에 비해 월등하지 않기 때문에 주어진 상황과 요구사항을 파악하고, 내가 선택하고자 하는 방식의 트레이드오프를 이해하여 신중한 선택을 하도록 합시다.

Last updated