복제 vs 파티셔닝
복제
- 동일한 데이터 복사본 여러 개를 다른 노드에 저장
파티셔닝
- 데이터를 파티션으로 나누어 저장 (이러한 작업을 샤딩이라고 한다)
보통 복제와 파티셔닝을 함께 적용해 각 파티션의 복사본을 여러 노드에 저장한다.
- 각 레코드는 정확히 한 파티션에 속하더라도 이를 여러 다른 노드에 저장해서 내결함성을 보장할 수 있다.
파티션을 하는 이유?
파티션을 하는 주된 이유는 확장성이다.
- 비공유 클러스터에서 다른 파티션은 다른 노드에 저장될 수 있다.
- 따라서 대용량 데이터셋이 여러 디스크에 분산될 수 있고 질의 부하는 여러 프로세서에 분산될 수 있다.
key-value 데이터 파티셔닝
대량의 데이터를 파티셔닝 하려 할 때, 어떤 레코드를 어느 노드에 저장해야 할까?
파티셔닝의 목적은 데이터와 질의 부하를 노드 사이에 고르게 분산시키는 것이다.
파티셔닝이 고르게 이뤄지지 않아 다른 파티션보다 데이터가 많거나 질의를 많이 받는 파티션이 있다면 쏠렸다고 한다. (불균형하게 부하가 높은 파티션을 핫스팟이라고 한다)
파티셔닝 방법
핫스팟을 회피하는 가장 단순한 방법은 레코드를 할당할 노드를 무작위로 선택하는 것이다.
- 그러면 데이터가 노드 사이에 매우 고르게 분산되지만, 어떤 레코드를 읽으려고 할 때 해당 레코드가 어느 노드에 저장됐는지 알 수 없으므로 모든 노드에서 병렬적으로 질의를 실행해야 한다.
키 범위 기준 파티셔닝
- 각 파티션에 연속된 범위의 키를 할당하는 것이다.
- 예를 들어 0~9로 시작하는 단어가 있을 때, 0~2으로 시작하는 단어는 파티션 A에, 3~6로 시작하는 단어는 파티션 B에, 7~9로 시작하는 단어는 파티션 C에 할당하는 것이다.
- 파티션 경계는 관리자가 수동으로 선택하거나 데이터베이스에서 자동으로 선택되게 할 수 있다.
- 이런 식으로 파티셔닝하는 전략은 빅테이블, HBase, RethinkDB, MongoDB(버전 2.4 이전)에서 사용된다.
키 범위 기준 파티셔닝의 장점
- 각 파티션 내에서는 키를 정렬된 순서로 저장할 수 있다.
- 이렇게 하면 범위 스캔이 쉬워지는 이점이 있고, 키를 연쇄된 색인으로 간주해서 질의 하나로 관련 레코드 여러 개를 읽어오는 데 사용할 수 있다.
키 범위 기준 파티셔닝의 단점
- 특정한 접근 패턴이 핫스팟을 유발하는 단점이 있음
키의 해시 값 기준 파티셔닝
- 쏠림과 핫스팟의 위험 때문에 많은 분산 데이터 스토어는 키의 파티션을 정하는 데 해시 함수를 사용한다.
- 키에 적합한 해시 함수를 구했다면 각 파티션 해시 값 범위를 할당하고, 해시 값이 파티션의 범위에 속하는 모든 키를 그 파티션에 할당하면 된다.
장점
- 키를 파티션 사이에 균일하게 분산시키는 데 좋음.
- 파티션 경계는 크기가 동일하도록 나눌 수도 있고 무작위에 가깝게 선택할 수 있다. (일관성 해싱이라고 부름)
단점
- 범위 질의를 효율적으로 실행할 수 없음.
- 정렬 순서가 유지되지 않는다.
사례
- 몽고DB에서 해시 기반 샤딩 모드를 활성화하면 범위 질의가 모든 파티션에 전송되어야 한다. 리악, 카우치베이스, 볼드모트에서는 기본키에 대한 범위 질의가 지원되지 않는다.
- 카산드라는 두 가지 파티셔닝 전략 사이에서 타협한다.
- 카산드라에서 테이블을 선언할 때 여러 칼럼을 포함하는 복합 기본키를 지정할 수 있다.
- 키의 첫 부분에만 해싱을 적용해 파티션 결정에 사용하고 남은 칼럼은 카산드라의 SS테이블에서 데이터를 정렬하는 연쇄된 색인으로 사용한다.
- 따라서 복합 키의 첫 번째 칼럼에 대해서는 값 범위로 검색하는 질의를 쓸 수 없지만, 첫 번째 칼럼에 고정된 값을 지정하면 키의 다른 칼럼에 대해서는 범위 스캔을 효율적으로 실행할 수 있다.
쏠림 작업 부하와 핫스팟 완화
- 키를 해싱해서 파티션을 정하면 핫스팟을 줄이는 데 도움이 되지만 완벽히 핫스팟을 제거할 수는 없다.
- 현대 데이터 시스템은 대부분 크게 쏠린 작업 부하를 자동으로 보정하지 못하므로 애플리케이션에서 쏠림을 완화해야 한다. (예를 들어 요청이 매우 많이 쏠리는 키를 발견했을 때 각 키의 시작이나 끝에 임의의 숫자를 붙이는 것)
파티셔닝과 보조 색인
보조 색인은 보통 레코드를 유일하게 식별하는 용도가 아니라 특정한 값이 발생한 항목을 검색하는 수단이다.
- 보조 색인은 관계형 데이터베이스의 핵심 요소이며 문서 데이터베이스에서도 흔하다.
- 많은 키-값 저장소에서는 구현 복잡도가 추가되는 것을 피하려고 보조 색인을 지원하지 않지만 보조 색인은 데이터 모델링에 매우 유용하므로 일부 저장소에서는 이를 추가하기 시작했다.
- 보조색인은 솔라나 ElasticSearch 같은 검색 서버에서는 존재의 이유다.
보조 색인은 파티션에 깔끔하게 대응되지 않는 문제점이 있다.
보조색인이 있는 데이터베이스를 파티셔닝 하는 데 널리 쓰이는 두 가지 방법이 있다.
- 문서 기반 파티셔닝
- 용이 기반 파티셔닝
문서 기반 파티셔닝
- 보조 색인을 기본키와 값이 저장된 파티션에 저장한다.
- MongoDB, 리악, Cassandra, ElasticSearch 등은 문서 기준으로 파티셔닝 된 보조 색인을 사용한다.
장점
- 데이터베이스에 문서 추가, 삭제, 갱신 등의 쓰기 작업을 실행할 때는 쓰려고 하는 문서 ID를 포함하는 파티션만 다루면 된다 (이러한 특성으로 지역 색인이라고도 함)
단점
- 문서 기반 파티셔닝에서 보조 색인을 읽으려면 모든 파티션에 걸쳐서 scatter/getter를 실행해야 한다.
(scatter/getter 방식은 모든 파티션으로 질의를 보내서 얻을 결과를 모두 모으는 것을 의미한다) - 이러한 이유로 보조 색인을 써서 읽는 질의는 큰 비용이 들 수 있다.
용어 기준 보조 색인 파티셔닝
각 파티션이 자신만의 보조 색인을 갖게 하는 대신, 모든 파티션의 데이터를 담당하는 전역 색인을 만들 수 있다.
- 색인된 값을 사용해서 보조 색인을 별도로 파티셔닝.
- 기본키의 모든 파티션에 있는 레코드를 포함할 수도 있다.
장점
- 모든 파티션에 스캐터/개터를 실행할 필요 없이, 원하는 용어를 포함하는 파티션으로만 요청을 보내면 되므로, 읽기가 효율적이다.
단점
- 쓰기가 느리고 복잡하다는 단점이 존재.
- 단일 문서를 쓸 때 해당 색인의 여러 파티션에 영향을 줄 수 있기 때문이다.
- 이상적으로는 색인은 항상 최신 상태에 있고 데이터베이스에 기록된 모든 문서는 바로 색인에 반영돼야 한다. 하지만 용어 파티셔닝 색인을 사용할 때 그렇게 하려면 쓰기에 영향을 받는 모든 파티션에 걸친 분산 트랜잭션을 실행해야 하는데, 모든 데이터베이스에서 분산 트랜잭션을 지원하지는 않는다.
- 현실에서는 전역 보조 색인은 대게 비동기로 갱신된다.(쓰기를 실행한 후 바로 색인을 읽으면 변경 사항이 색인에 반영되지 않았을 수도 있다)
파티션 리밸런싱
리밸런싱이란?
시간이 지나면서 다음과 같은 여러 사항으로 데이터와 요청이 한 노드에서 다른 노드로 옮겨져야 한다.
- 질의 처리량이 증가해서 늘어난 부하를 처리하기 위해 CPU를 더 추가한다.
- 데이터셋 크기가 증가해서 데이터셋 저장에 사용할 디스크와 램을 추가한다.
- 장비에 장애가 발생해서 그 장비가 담당하던 역할을 다른 장비가 넘겨받아야 한다.
클러스터에서 한 노드가 담당하던 부하를 다른 노드로 옮기는 과정을 리밸런싱이라고 한다.
리밸런싱 최소 요구사항
- 리밸런싱 이후, 부하가 클러스터 내에 있는 노드들 사이에 균등하게 분배돼야 한다.
- 리밸런싱 도중에도 데이터베이스는 읽기 쓰기 요청을 받아들여야 한다.
- 리밸런싱이 빨리 실행되고, 네트워크와 디스크 I/O 부하를 최소화할 수 있도록 노드들 사이에 데이터가 필요 이상으로 옮겨져서는 안 된다.
리밸런싱 전략
1. 해시값에 모드 N 연산을 실행 (쓰면 안 되는 방법)
- mod 연산 (%)을 쓰면 안되는 이유는 노드 개수가 바뀌면 대부분의 키가 노드 사이에 옮겨져야 하기 때문이다. (리밸런싱 비용이 지나치게 커진다)
2. 파티션 개수 고정
- 파티션을 노드 대수보다 많이 만들고 각 노드에 여러 파티션을 할당하는 것.
- (예를 들어 노드 10대로 구성된 클러스터에서 실행되는 데이터베이스는 처음부터 파티션을 1000개로 쪼개서 각 노드마다 100개의 파티션을 할당할 수 있다)
- 클러스터에 노드가 추가되면 새 노드는 파티션이 다시 균일하게 분배될 때까지 기존 노드에서 파티션 몇 개를 뺏어올 수 있다. (노드가 제거되면 반대로 실행)
- 파티션은 노드 사이에서 통째로 이동하기만 한다.
- 파티션 개수는 바뀌지 않고 파티션에 할당된 키도 변경되지 않는다.
- 유일한 변화는 노드에 어떤 파티션이 할당되는가 뿐이다.
- 이 방식을 사용할 때, 보통 데이터베이스가 처음 구축될 때 파티션 개수가 고정되고 이후에 변하지 않는다.
- 처음 설정된 파티션 개수가 사용 가능한 노드 대수의 최대치가 되므로 미래에 증가될 것을 수용하기에 충분히 높은 값으로 선택해야 한다. 그러나 개별 파티션도 관리 오버헤드가 있으므로 너무 큰 수를 선택하면 역효과를 낳을 수 있다.
3. 동적 파티셔닝
- 키 범위 파티셔닝을 사용하는 데이터베이스에서는 파티션 경계와 개수가 고정돼 있는 게 매우 불편하다.
- 파티션 경계를 잘못 지정하면 모든 데이터가 한 파티션에 저장되고 나머지 파티션은 텅 빌 수도 있다.
- 이러한 이유로 HBase나 리싱크 DB처럼 키 범위 파티셔닝을 사용하는 데이터베이스에서는 파티션을 동적으로 만든다.
- 파티션 크기가 설정된 값을 넘어서면 파티션을 두 개로 쪼개 각각에 원래 파티션의 절반 정도의 데이터가 포함되게 한다.
- 반대로 데이터가 많이 삭제되어 파티션 크기가 임계값 아래로 떨어지면 인접한 파티션과 합쳐질 수 있다.
동적 파티셔닝은 파티션 개수가 전체 데이터 용량에 맞춰 조정된다는 이점이 있다.
- 데이터 양이 작으면 파티션 개수가 적어도 되므로 오버헤드도 작다.
그러나 빈 데이터베이스는 파티션 경계를 어디로 정해야 하는지 사전 정보가 없으므로 시작할 때는 파티션이 하나라는 함정이 있다.
- 이 문제를 완화하기 위해 HBase와 몽고 DB에서는 빈 데이터베이스에 초기 파티션 집합을 설정할 수 있게 한다. (사전 분할이라고 한다)
4. 노드 비례 파티셔닝
- 카산드라와 케타마에서 사용되는 방법으로, 파티션 개수가 노드 대수에 비례하게 하는 방법. (노드당 할당되는 파티션 개수를 고정하는 것)
- 노드 대수가 변함없는 동안은 개별 파티션 크기가 데이터셋 크기에 비례해서 증가하지만 노드 대수를 늘리면 파티션 크기는 다시 작아진다.
- 새 노드가 클러스터에 추가되면 고정된 개수의 파티션을 무작위로 선택해 분할하고 각 분할된 파티션의 절반은 그대로 두고 다른 절반은 새 노드에 할당한다.
요청 라우팅
- 클라이언트에서 요청을 보내려고 할 때 어느 노드로 접속해야 하는지 어떻게 알 수 있을까?
- 파티션이 리밸런싱 되면 노드에 할당되는 파티션이 바뀐다.
접근 방법
- 클라이언트가 아무 노드에나 접속하게 해서 만약 해당 노드에 요청을 적용할 파티션이 있다면 거기서 요청을 직접 처리하고 그렇지 않은 경우 올바른 노드로 전달해서 응답을 받고 클라이언트에게 응답을 전달. (코디네이터 접근법?)
- 클라이언트의 모든 요청을 라우팅 계층으로 먼저 보내서, 라우팅 계층에서 해당 노드로 요청을 전달하는 방식.
- 클라이언트가 파티셔닝 방법과 파티션이 어느 노드에 할당됐는지를 알게 하는 방
모든 경우에 핵심 문제는 라우팅 결정을 내리는 구성 요소가 노드에 할당된 파티션의 변경 사항을 어떻게 아느냐다.
- 이 문제는 참여하는 모든 곳에서 정보가 일치해야 하므로 다루기 어렵다. 그렇지 않으면 요청이 잘못된 노드로 전송되고 제대로 처리되지 못하기 때문이다.
많은 분산 데이터 시스템은 클러스터 메타데이터를 추적하기 위해 Zookeeper 같은 별도의 코디네이터 서비스를 사용한다.
- 각 노드는 주키퍼에 자신을 등록하고 주키퍼는 파티션과 노드 사이의 신뢰성 있는 할당 정보를 관리한다.
- 라우팅 계층이나 파티션 인지 클라이언트 같은 다른 구성요소들은 주키퍼에 있는 정보를 구독할 수 있다.
사례
- HBase, 솔라클라우드, 카프카도 파티션 할당을 추적하는 데 주키퍼를 사용한다.
- MongoDB도 아키텍처는 비슷하지만 자체적인 설정 서버 구현에 의존하고, 몽고스 데몬을 라우팅 계층으로 사용한다.
- 카산드라와 리약은 가십 프로토콜을 사용해서 클러스터 상태 변화를 노드 사이에 퍼뜨린다.
- 아무 노드나 요청을 받을 수 있고 요청을 받은 노드는 요청을 처리할 파티션을 갖고 있는 올바른 노드로 요청을 전달해준다.
'공통 > 인프라 & 시스템 설계' 카테고리의 다른 글
[데이터 중심 애플리케이션 설계 정리] 분산 시스템의 어려움, 정족수, 펜싱 토큰 개념 (0) | 2022.03.27 |
---|---|
[대규모 시스템 설계 기초 정리] 피드 시스템 설계 (0) | 2022.01.27 |
[대규모 시스템 설계 기초 정리] 분산 키-값 저장소 with Cassandra DB (0) | 2022.01.18 |
[대규모 시스템 설계 기초 정리 + Cassandra] 안정 해시 설계 + Cassandra Virtual Node (0) | 2022.01.18 |
[대규모 시스템 설계 기초 정리] 알림 시스템 설계 (0) | 2021.11.01 |