////
Search

6장 - 파티셔닝

Date
2025/01/17 09:00
Tags

1. 파티셔닝

데이터셋이 매우 크거나 질의 처리량이 매우 높다면 복제만으로는 부족하고 데이터를 파티션으로 쪼갤 필요가 있다.
이 작업을 파티셔닝 또는 샤딩이라고 한다.
파티션을 나눌 때는 보통 각 데이터 단위(레코드, 로우, 문서)가 하나의 파티션에 속하게 한다.
데이터베이스가 여러 파티션을 동시에 건드리는 연산을 지원할 수도 있지만 결과적으로 각 파티션은 그 자체로 작은 데이터베이스가 된다.
데이터 파티셔닝을 원하는 주된 이유는 확장성이다.
단일 파티션에 실행되는 질의를 생각해 보면 각 노드에서 자신의 파티션에 해당하는 질의를 독립적으로 실행할 수 있으므로 노드를 추가해 처리량을 늘릴 수 있다.

1.1. 파티셔닝과 복제

복제와 파티셔닝 조합
보통 복제와 파티셔닝을 함께 적용해 각 파티션의 복사본을 여러 노드에 저장한다.
각 레코드는 정확히 한 파티션에 속하더라도 이를 여러 다른 노드에 저장해서 내결합성을 보장할 수 있다는 의미이다.
한 노드에 여러 파티션을 저장할 수도 있다.

1.2. 키-값 데이터 파티셔닝

파티셔닝의 목적은 데이터와 질의 부하를 노드 사이에 고르게 분산시키는 것이다.
파티셔닝이 고르게 이뤄지지 않아 데이터가 많거나 질의를 많이 받는 파티션이 있다면 쏠렸(skewed)다고 한다.
불균형하게 부하가 높은 파티션을 핫스팟(hotspot) 이라고 부름
회피하는 방법은 레코드를 할당할 노드를 무작위로 선택하는 것
더 좋은 키-값 데이터 모델 = 항상 기본키를 통해 레코드에 접근한다.

1.2.1. 키 범위 파티셔닝

키를 기준으로 파티셔닝된 백과사전
각 파티션에 연속된 범위의 키를 할당하는 것이다.
각 범위들 사이의 경계를 알면 어떤 키가 어느 파티션에 속하는지 쉽게 찾을 수 있다.
어떤 파티션이 어느 노드에 할당됐는지 알면 적절한 노드로 요청을 직접 보낼 수 있다.
데이터가 고르게 분포하지 않을 수 있기 때문에 키 범위 크기가 반드시 동일 할 필요는 없다.
장점
각 파티션 내에서는 키를 정렬된 순서로 저장할 수 있다.
이렇게 하면 범위 스캔이 쉬워지는 이점이 있고 키를 연쇄적 색인으로 간주해서 질의 하나로 관련 레코드 여러 개를 읽어 오는데 사용할 수 있다.
단점
특정한 접근 패턴이 핫스팟을 유발한다.
reg_date가 키라면 파티션은 년(year) 범위에 대응된다. 예를 들어 1년의 데이터를 파티션 하나가 담당하는 식이다.
데이터를 갱신할 때 마다 데이터베이스에 기록하므로 쓰기 연산이 모두 동일한 파티션(해당하는 year)으로 전달되어 해당 파티션만 과부하가 걸리고 나머지 파티션은 유휴 상태로 남을 수 있다.

1.2.2. 키의 해시값 기준 파티셔닝

키의 해시값 기준 파티셔닝
해시 파티션은 특정 해시 함수에 의해 레코드가 저장될 파티션을 결정하는 방법이다.
파티셔닝용 해시 함수는 암호적으로 강력할 필요는 없다.
장점
쏠림과 핫스팟의 위험을 줄여 쏠린 데이터를 입력으로 받아 균일하게 분산되게 한다.
단점
범위 질의를 효율적으로 실행할 수 있는 키 범위 파티셔닝의 좋은 속성을 잃어버린다.
전에는 인접했던 키들이 이제는 모든 파티션에 흩어져서 정렬 순서가 유지되지 않는다.

1.2.3. 쏠린 작업부하와 핫스팟 완화

키를 해싱하면 핫스팟을 줄이는데 도움은 되지만 완벽히 제거할 수는 없다.
항상 동일한 키를 읽고 쓰는 극단적인 상황에서는 모든 요청이 동일한 파티션으로 쏠리게 된다.
해결책
각키의 시작이나 끝에 임의의 숫자를 붙인다.
임의의 10진수 두 개만 붙이더라도 한 키에 대한 쓰기 작업이 100개의 다른 키로 균등하게 분산되고 그 키들은 다른 파티션으로 분산될 수 있다.
그러나 다른 키에 쪼개서 쓰면 읽기를 실행할 때 추가적인 작업이 필요해진다.
100개의 키에 해당 하는 데이터를 읽어서 조합해야 하기 때문이다. 추가적으로 저장해야 할 정보도 있다.
이 기법은 요청이 몰리는 소수의 키에만 적용하는게 타당하다.
쓰기 처리량이 낮은 대다수의 키에도 적용하면 불필요한 오버헤드가 생긴다. 따라서 어떤 키가 쪼개졌는지 추적할 방법도 있어야 한다.

2. 파티셔닝과 보조 색인

레코드를 기본키를 통해서만 접근한다면 키로부터 파티션을 결정하고 이를 사용해 해당 키를 담당하는 파티션으로 읽기 쓰기 요청을 전달할 수 있다.
그러나 특정한 값이 발생한 항목을 검색하는 수단인 보조색인이 연관되면 복잡해 진다.
보조 색인은 파티션에 깔끔하게 대응되지 않는 문제점이 있기 때문이다.

2.1. 문서 기준 보조 색인 파티셔닝

문서 기준 보조 색인 파티셔닝
각 항목은 문서ID라는 고유 ID가 있고 해당 ID를 기준으로 파티셔닝 한다.
각 파티션이 완전히 독립적으로 동작하기 때문에 보조 색인을 유지하며 그 파티션에 속하는 문서만 담당한다.
문서 파티셔닝 색인은 지역 색인(Local Index)이라고 한다.
주의점
문서 ID에 뭔가 특별한 작업을 하지 않는다면 특정한 색상이거나 특정한 제조사가 만든 자동차가 동일한 파티션에 저장되리라는 보장이 없다.
위 이미지를 보면 빨간색 자동차는 파티션 0에도 있고 파티션 1에도 있다.
따라서 빨간색 자동차를 찾고 싶다면 모든 파티션으로 질의를 보내서 얻은 결과를 모두 모아야 한다.
파티셔닝된 데이터베이스에 이런 식으로 질의를 보내는 방법을 스캐터/개더(scatter/gather)라고도 하는데 보조 색인을 써서 읽는 질의는 큰 비용이 들 수 있다.

2.2. 용어 기준 보조 색인 파티셔닝

용어 기준 보조 색인 파티셔닝
지역 색인과 다르게 모든 파티션의 데이터를 담당하는 색인을 전역 색인을 만들 수도 있다.
그러나 한 노드에만 색인을 저장할 수는 없다.
해당 노드가 병목이 되어 파티셔닝의 목적을 헤칠 수 있기 때문에 여러 노드에 나눠서 저장한다.
찾고자 하는 용어에 따라 색인의 파티션이 결정되므로 이런 식의 색인을 용어 기준으로 파티셔닝됐다(term-partitioned)고 한다.
용어란 문선에 등장하는 모든 단어를 말한다.
장점
문서 파티셔닝 색인에 비해 전역(용어 파티셔닝) 색인이 갖는 이점은 읽기가 효율적이라는 것이다.
클라이언트는 모든 파티션에 스개터/개더를 실행할 필요 없이 원하는 용어를 포함하는 파티션으로만 요청을 보내면 된다.
단점
단일 문서를 쓸 때 해당 색인의 여러 파티션에 영향을 줄 수 있으며 쓰기가 느리고 복잡하다.
단일 문서를 쓸 때 해당 색인의 여러 파티션에 영향을 줄 수 있기 때문이다.
이상적으로는 색인은 항상 최신 상태에 있고 데이터베이스에 기록된 모든 문서는 바로 색인에 반영돼야 한다.
하지만 용어 파티셔닝 색인을 사용할 때 그렇게 하려면 쓰기에 영향받는 모든 파티션에 걸친 분산 트랜잭션을 실행해야 하는데, 모든 데이터베이스에서 분산 트랜잭션을 지원하지는 않는다.
현실에서는 전역 보조 색인은 대개 비동기로 갱신된다.
즉 쓰기를 실행한 후 바로 색인을 읽으면 변경 사항이 색인에 반영되지 않았을 수도 있다.

3. 파티션 재균형화

시간이 지남에 따라 DB에 변화가 생긴다.
질의 처리량이 증가해서 늘어난 부하를 처리하기 위해 CPU를 더 추가하고 싶다.
데이터셋 크기가 증가해서 데이터셋 저장에 사용할 디스크와 램을 추가하고 싶다.
장비에 장애가 발생해서 그 장비가 담당하던 역할을 다른 장비가 넘겨받아야 한다.
재균형화(rebalancing) = 클러스터에서 한 노드가 담당하던 부하를 다른 노드로 옮기는 과정을
재균형과가 실행될 때 만족시킬 것으로 기대되는 최소 요구사항
리밸런싱 이후, 부하가 클러스터 내에 있는 노드들 사이에 균등하게 분배돼야 한다.
리밸런싱 도중에도 데이터베이스는 읽기 쓰기 요청을 받아들여야 한다.
리밸런싱이 빨리 실행되고, 네트워크와 디스크 I/O 부하를 최소화할 수 있도록 노드들 사이에 데이터가 필요 이상으로 옮겨져서는 안 된다.

3.1. 재균형화 전략

3.1.1. 쓰면 안되는 전략 : 해시값에 mod(N) 연산을 실행

mod(N) 방식의 문제는 노드 개수 N이 바뀌면 대부분의 키가 노드 사이에 옮겨져야 한다는 점이다.
키가 자주 이동하면 재균형화 비용이 지나치게 커진다.

3.1.2. 파티션 개수 조정

파티션을 노드 대수보다 많이 만들고 각 노드에 여러 파티션을 할당하는 방법
예를 들어 노드 10대에 파티션 1000개
클러스터에 노드가 추가되면 새 노드는 파티션이 다시 균일하게 분배될 때까지 기존 노드에서 파티션 몇 개를 뺏어올 수 있다.
노드가 제거될 경우 반대로 실행된다.
파티션은 노드 사이에서 통째로 이동하기만 한다.
파티션 개수는 바뀌지 않고 파티션에 할당된 키도 변경되지 않는다.
유일한 변화는 노드에 어떤 파티션이 할당되는가 뿐이다.
이 방식을 사용할 때, 보통 데이터베이스가 처음 구축될 때 파티션 개수가 고정되고 이후에 변하지 않는다.
처음 설정된 파티션 개수가 사용 가능한 노드 대수의 최대치가 되므로 미래에 증가될 것을 수용하기에 충분히 높은 값으로 선택해야 한다.
그러나 개별 파티션도 관리 오버헤드가 있으므로 너무 큰 수를 선택하면 역효과를 낳을 수 있다.

3.1.3. 동적 파티셔닝

키 범위 파티셔닝을 사용하는 DB에서는 파티션 경계와 개수가 고정돼 있으면 매우 불편하다.
경계를 잘못 지정하면 모든 데이터가 한 파티션에 저장되고 나머지 파티션은 빌 수도 있기 때문이다.
이런 이유로 HBase처럼 키 범위 파티셔닝을 사용하는 DB에서는 파티션을 동적으로 만든다.
파티션 크기의 설정값에 따라 쪼개지거나 합쳐진다 (B트리와 유사)
동적 파티셔닝은 파티션 개수가 전체 데이터 용량에 맞춰 조정된다는 이점이 있다.
데이터 양이 작으면 파티션 개수가 적어도 되므로 오버헤드도 작다.
But 비어있는 DB는 파티션 경계를 어디로 정해야 하는지에 관한 사전 정보가 없으므로 시작할 때는 파티션이 1개라는 함정이 있다.
이런 문제를 해결하기 위해 HBase와 몽고DB에서는 빈 DB에 초기 파티션 집합을 설정할 수 있는 기능을 제공

3.1.4. 노드 비례 파티셔닝

동적 파티셔닝에서는 파티션 분할과 병합을 통해 개별 파티션 크기가 어떤 고정된 최솟값과 최댓값 사이에 유지되게 하므로 파티션 개수가 데이터셋 크기에 비례한다.
반면 파티션 개수를 고정하면 개별 파티션의 크기가 데이터셋 크기에 비례한다.
두 경우 모두 파티션 개수는 노드 대수와 독립적이다.
파티션 개수가 노드 대수에 비례하게 하는 방법
노드당 할당되는 파티션 개수를 고정한다.
노드 대수가 변함없는 동안은 개별 파티션 크기가 데이터셋 크기에 비례해서 증가하지만 노드 대수를 늘리면 파티션 크기는 다시 작아진다.
일반적으로 데이터 용량이 클수록 데이터를 저장할 노드도 많이 필요하므로 이 방법을 쓰면 개별 파티션 크기도 상당히 안정적으로 유지된다.
새 노드가 클러스터에 추가되면 고정된 개수의 파티션을 무작위로 선택해 분할하고 각 분할된 파티션의 절반은 그대로 두고 다른 절반은 새 노드에 할당한다.
파티션을 랜덤으로 선택해 균등하지 않은 분할이 생길 수 있지만 여러 파티션에 대해 평균적으로 보면 새 노드는 기존 노드들이 담당하던 부하에서 균등한 몫을 할당받게 된다.

3.2. 운영: 자동 재균형화와 수동 재균형화

재균형화는 자동으로 실행될까? 아니면 수동으로 실행해야 할까?
완전 자동 재균형화와 완전 수동 재균형화 사이에는 중간 지점이 있다.
예를 들어 카우치베이스, 리악, 볼드모트는 자동으로 파티션 할당을 제안하지만 반영되려면 관리자가 확정해야 한다.

3.2.1. 완전 자동 재균형화

완전 자동 재균형화는 일상적인 유지보수에 손이 덜 가므로 편리할 수 있다.
하지만 예측하기 어렵다.
재균형화는 요청 경로를 재설정해야 하고 대량의 데이터를 노드 사이에 이동해야 하므로 비용이 큰 연산이다.
재균형화 과정에서 사람이 개입하는 게 좋을 수 있다.
완전 자동 처리보다는 느릴 수 있지만 운영상 예상치 못한 일을 방지하는 데 도움이 될 수 있기 때문

3.3. 요청 라우팅

클라이언트에서 요청을 보내려고 할 때 어느 노드로 접속해야할까?
파티션이 재균형화 되면서 노드에 할당되는 파티션이 바뀜 = 데이터 위치가 바뀐다는 의미

3.3.1. 서비스 찾기(service discovery)

데이터베이스에 국한되지 않은 일반적인 문제인 서비스 찾기의 일종
고가용성을 지향하는 소프트웨어라면 모두 가지고 있는 문제
접근법
클라이언트가 아무 노드에나 접속하게 한다.
요청을 적용할 파티션이 있다면 직접 처리를 한다.
만약 찾고자 하는 파티션이 아니었다면 올바른 노드로 전달해서 응답을 받고 클라이언트에게 응답을 전달한다.
클라이언트의 모든 요청을 라우팅 계층으로 먼저 보낸다.
라우팅 계층은 파티션을 정하는 로드밸런서로 동작만 할 뿐 그 자체에서는 아무 요청도 처리하지 않는다.
클라이언트가 파티셔닝 방법과 파티션이 어떤 노드에 할당됐는지를 알고 있게 한다.
중개자 없이 올바른 노드로 직접 접속한다.
이 문제는 참여하는 모든 곳에서 정보가 일치해야 하므로 다루기 어렵다.
그렇지 않으면 요청이 잘못된 노드로 전송되고 제대로 처리되지 못한다.
또한 분산 시스템에서 합의를 이루는 데 쓰이는 프로토콜이 있지만 제대로 구현하기가 까다롭다.

3.3.2. 코디네이터

많은 분산 데이터 시스템은 클러스트 메타데이터를 추적하기 위해 주키퍼(ZooKeeper)와 같은 별도의 코디네이션 서비스를 사용한다.
각 노드는 주키퍼에 자신을 등록하고 주키퍼는 파티션과 노드 사이의 신뢰성 있는 정보를 관리한다.
그래서 파티션 소유자가 바뀌거나 클러스터에 노드가 추가 혹은 삭제가 되면 주키퍼는 라우팅 계층에 이를 알려서 라우팅 정보를 최신으로 유지할 수 있게 한다.
그리고 이 라우팅 계층을 바라보는 다른 구성요소들은 주키퍼에 있는 정보를 구독한다.