레플리카 샤드
고친 사람 github-actions[bot]
레플리카 샤드는 데이터 조각 하나를 다른 서버에 한 벌 더 둡니다. 원본을 든 서버가 죽어도 그 조각의 데이터를 잃지 않게 합니다. 평소에는 검색 요청을 나눠 받아 읽는 부담도 덜어 줍니다. 원본 조각이 받은 쓰기를 뒤따라 받아서 원본과 같은 내용을 지닙니다.
쉽고 빠른 이해
레플리카 샤드는 원본 조각의 복사본을 다른 서버에 두는 일을 합니다. 데이터를 조각 셋으로 나눈다고 해 봅시다. 조각마다 복사본을 하나씩 두면 서버들 위에 원본 셋과 복사본 셋이 흩어져 놓입니다.
복사본이 없으면 서버 한 대가 죽을 때 그 서버가 들고 있던 조각의 문서를 찾을 수 없습니다. 조각이 하나 빠진 채로는 검색 결과도 빠진 채로 나옵니다.
어떻게 도나:
- 문서를 쓰는 요청은 원본 조각이 먼저 받습니다
- 원본이 같은 쓰기를 복사본에 보냅니다. 복사본까지 적은 뒤에 성공을 돌려줍니다
- 원본이 든 서버가 죽으면 복사본 하나가 새 원본이 되어 일을 이어 갑니다
대가는 디스크와 쓰기 일이 복사본 수만큼 늘어난다는 것입니다. 서버 수가 모자라면 복사본을 둘 곳이 없어 만들어지지도 않습니다.
상세
중요한 서류는 사본을 떠서 다른 건물에 둡니다. 한 건물에 불이 나도 다른 건물의 사본으로 일을 이어 갑니다. 평소에는 두 건물에서 같은 서류를 따로 열람할 수 있습니다.
이 절은 먼저 레플리카 샤드가 무엇의 복사본인지 가릅니다. 이어서 쓰기 한 번이 원본에서 복사본으로 건너가는 순서와, 원본이 든 서버가 죽을 때 벌어지는 일을 따라갑니다. 그다음 복사본을 몇 벌 둘지 정하는 설정과 그 대가를 봅니다.
샤드와 프라이머리 샤드
데이터가 서버 한 대에 다 안 들어가면 문서를 건수로 나눠 여러 서버에 흩어 둡니다. 이렇게 나눈 조각 하나를 샤드라고 부릅니다.
샤드를 들고 있는 서버 하나를 노드라고 부릅니다. 노드 한 대가 여러 샤드를 함께 들기도 합니다.
노드들을 한데 묶은 무리는 클러스터입니다. 어느 샤드를 어느 노드에 둘지는 클러스터가 정합니다.
검색 엔진에서는 문서를 담는 묶음 하나를 인덱스라고 부릅니다. 관계형 데이터베이스의 테이블에 가까운 단위입니다. 인덱스 하나가 샤드 여럿으로 나뉩니다.
조각마다 원본이 하나씩 있습니다. 이 원본 조각을 프라이머리 샤드라고 부릅니다. 주 샤드라고도 부릅니다. 레플리카 샤드는 프라이머리 샤드 하나를 빠짐없이 베낀 복사본입니다. 원본이 담은 문서를 전부 담고, 원본과 똑같이 검색할 수 있습니다.
이 글에서 원본이라고 하면 프라이머리 샤드를, 복사본이라고 하면 레플리카 샤드를 가리킵니다. 조각 하나의 원본과 복사본을 한데 셀 때는 몇 벌이라고 셉니다.
같은 데이터를 여러 곳에 베껴 두는 일을 복제라고 부릅니다. 레플리카(replica)라는 이름도 복제본이라는 뜻입니다. 샤드가 데이터를 나누는 축이라면, 레플리카 샤드는 나눈 조각을 한 벌씩 더 두는 축입니다.
원본과 다른 노드에 두는 까닭
레플리카 샤드는 자기 프라이머리 샤드와 같은 노드에 놓이지 않습니다. 둘이 한 노드에 같이 있으면 그 노드가 죽을 때 원본과 복사본이 함께 사라집니다. 복사본을 둔 까닭이 없어집니다.
아래 그림은 프라이머리 샤드 셋에 복사본을 하나씩 두고 노드 셋에 나눠 놓은 모습입니다. 노드마다 어느 조각의 원본과 다른 조각의 복사본을 섞어 듭니다.
flowchart TD
subgraph N1["노드 1"]
P0["프라이머리 샤드 0"]
R2["레플리카 샤드 2"]
end
subgraph N2["노드 2"]
P1["프라이머리 샤드 1"]
R0["레플리카 샤드 0"]
end
subgraph N3["노드 3"]
P2["프라이머리 샤드 2"]
R1["레플리카 샤드 1"]
end
P0 -.베낀다.-> R0
P1 -.베낀다.-> R1
P2 -.베낀다.-> R2
어느 노드 하나를 지워 봐도 조각 0, 1, 2 가 다른 노드에 한 벌씩 남습니다. 노드 1 이 죽으면 조각 0 은 노드 2 의 복사본으로, 조각 2 는 노드 3 의 원본으로 남습니다.
쓰기 한 번이 건너가는 순서
문서를 넣거나 고치거나 지우는 요청은 프라이머리 샤드가 먼저 받습니다. 원본은 자기 쪽에 먼저 적습니다. 그다음 같은 쓰기를 복사본들에 보냅니다. 복사본들이 적었다고 답하면 그제야 요청한 쪽에 성공을 돌려줍니다.
sequenceDiagram
participant 요청 as 요청한 쪽
participant 원본 as 프라이머리 샤드
participant 사본 as 레플리카 샤드
요청->>원본: 문서를 쓴다
Note over 원본: 자기 쪽에 먼저 적는다
원본->>사본: 같은 쓰기를 보낸다
사본-->>원본: 적었다
원본-->>요청: 성공
쓰기가 늘 원본 한 곳을 거쳐 가므로 쓰기의 순서가 하나로 정해집니다. 원본과 복사본이 쓰기를 따로 받는다면 같은 문서를 고치는 두 요청이 서로 다른 순서로 적힐 수 있습니다. 그러면 원본과 복사본의 내용이 갈라집니다.
성공을 돌려주기 전에 복사본까지 기다리는 데도 까닭이 있습니다. 성공을 받은 문서는 이미 복사본에도 적혀 있습니다. 바로 다음에 원본 노드가 죽어도 그 문서는 복사본에 남아 있습니다.
읽기를 나눠 받는 복사본
검색이나 문서 한 건 조회는 원본과 복사본 가운데 어느 쪽이 받아도 됩니다. 둘이 같은 문서를 담고 있기 때문입니다. 그래서 같은 조각을 찾는 요청을 원본과 복사본이 나눠 받습니다.
복사본과 노드를 함께 늘리면 클러스터가 한꺼번에 받아 낼 수 있는 검색이 늘어납니다. 노드는 그대로 두고 복사본만 늘리면 같은 노드들이 더 많은 벌을 나눠 듭니다. 노드 한 대가 하는 일은 줄지 않습니다.
원본이 든 노드가 죽을 때
원본을 잃은 조각은 쓰기를 받을 곳이 없습니다. 그래서 클러스터는 남은 레플리카 샤드 하나를 새 프라이머리 샤드로 올립니다. 복사본이 원본 역할을 넘겨받는 이 일을 승격이라고 부릅니다.
승격이 끝나도 그 조각은 복사본이 한 벌 모자란 상태입니다. 클러스터는 살아 있는 다른 노드에 새 레플리카 샤드를 만들어 새 원본을 베낍니다. 다 베끼고 나면 복사본 수가 원래대로 돌아옵니다.
승격한 복사본에는 성공을 돌려준 쓰기가 전부 들어 있습니다. 앞에서 본 순서대로 복사본이 적은 뒤에야 성공이 나갔기 때문입니다.
복사본을 몇 벌 둘지 정하는 설정
레플리카 샤드의 수는 인덱스마다 정합니다. 프라이머리 샤드 하나마다 복사본을 몇 벌 둘지를 적는 설정입니다. 복사본이 한 벌이면 조각마다 원본과 복사본, 모두 두 벌이 생깁니다.
아래 표는 프라이머리 샤드가 셋인 인덱스에서 레플리카 수만 바꿔 본 것입니다. 「동시에 잃어도 되는 노드」 열은 「필요한 최소 노드」 열만큼 노드가 있을 때 성립합니다.
| 레플리카 수 | 조각마다 벌 수 | 전체 샤드 수 | 동시에 잃어도 되는 노드 | 필요한 최소 노드 |
|---|---|---|---|---|
| 0 | 1 | 3 | 0 | 1 |
| 1 | 2 | 6 | 1 | 2 |
| 2 | 3 | 9 | 2 | 3 |
레플리카 수가 하나 늘 때마다 동시에 잃어도 되는 노드가 하나 늘어납니다. 대신 노드도 그만큼 있어야 합니다. 같은 조각의 벌들은 서로 다른 노드에 놓여야 하기 때문입니다.
레플리카 수는 인덱스를 만든 뒤에도 언제든 바꿀 수 있습니다. 문서를 넣는 일과 검색을 멈추지 않은 채 바꿉니다. 늘리면 원본을 베껴 새 복사본을 만듭니다. 줄이면 복사본을 지웁니다.
프라이머리 샤드의 수는 다릅니다. 인덱스를 만들 때 정하고 나면 바꿀 수 없습니다. 문서가 어느 조각으로 갈지는 프라이머리 샤드의 수로 계산합니다. 그 수가 바뀌면 이미 넣은 문서를 찾아갈 길이 틀어집니다.
복사본이 치르는 값
복사본 한 벌은 원본만큼 디스크를 씁니다. 레플리카 수가 1 이면 인덱스가 차지하는 디스크가 두 배, 2 면 세 배가 됩니다.
쓰기 일도 늘어납니다. 쓰기 한 번을 모든 복사본이 저마다 받아 적습니다. 성공 응답도 가장 늦게 적은 복사본을 기다렸다가 나갑니다.
노드 수보다 벌 수가 많으면 남는 복사본은 놓일 노드가 없습니다. 그런 복사본은 만들어지지 않고 배정되지 않은 채로 남습니다. 이런 샤드를 미할당 샤드라고 부릅니다.
복사본을 두는 경우와 안 두는 경우
운영하는 클러스터에서는 복사본을 적어도 한 벌 둡니다. 노드 한 대가 죽어도 모든 조각이 한 벌씩 남게 하려는 것입니다.
처음에 문서를 대량으로 넣을 때는 레플리카 수를 0 으로 두었다가 다 넣은 뒤 늘리기도 합니다. 넣는 동안에는 쓰기가 원본에만 갑니다. 복사본은 적재가 끝난 뒤 원본을 한 번에 베껴 만듭니다.
노드가 하나뿐인 개발 환경에서는 복사본을 둘 노드가 없습니다. 이때는 레플리카 수를 0 으로 둡니다. 두어 봐야 미할당 샤드로 남을 뿐입니다.
이 이름을 쓰는 검색 엔진
레플리카 샤드라는 이름은 Elasticsearch 와, 거기서 갈라져 나온 OpenSearch 가 씁니다. 두
엔진 모두 인덱스 설정 index.number_of_replicas 로 복사본 수를 정합니다. 프라이머리 샤드 수는
index.number_of_shards 로 정합니다.
Elasticsearch 에서 레플리카 수의 기본값은 1 입니다. 이미 있는 인덱스의 복사본 수는 이렇게 바꿉니다.
PUT /orders/_settings
{
"index": {
"number_of_replicas": 2 // 복사본 2벌
}
}
orders 인덱스의 프라이머리 샤드마다 복사본이 두 벌씩 생깁니다. 원본까지 치면 조각마다 세 벌입니다.
복사본이 배정됐는지는 클러스터 헬스가 색으로 보여 줍니다.
| 색 | 뜻 |
|---|---|
| green | 모든 샤드가 배정됐다 |
| yellow | 프라이머리 샤드는 전부 배정됐고, 레플리카 샤드 가운데 배정 안 된 것이 있다 |
| red | 배정 안 된 프라이머리 샤드가 있다. 일부 데이터를 읽을 수 없다 |
노드 하나로 띄운 Elasticsearch 에 기본값 그대로 인덱스를 만들면 yellow 가 뜹니다. 복사본 한 벌은 원본과 다른 노드에 둬야 합니다. 노드가 하나뿐이라 둘 곳이 없습니다. 레플리카 수를 0 으로 내리면 green 이 됩니다.
샤드 안에서 복제하는 다른 설계
데이터베이스 가운데에는 조각마다 서버 묶음을 따로 두고 그 묶음 안에서 복제하는 것도 있습니다. MongoDB 는 샤드 하나를 레플리카 셋이라는 서버 묶음으로 띄웁니다. 이때는 복사본 하나하나를 샤드라고 부르지 않습니다. 묶음 전체를 샤드 하나로 봅니다.
레플리카 샤드 쪽은 노드 한 대가 여러 조각의 원본과 복사본을 섞어 듭니다. 서버 묶음 쪽은 한 묶음의 서버들이 한 조각만 맡습니다. 조각과 서버를 짝짓는 방식이 다릅니다. 조각마다 복사본을 둔다는 생각은 같습니다.
관련 항목
레플리카 샤드가 베끼는 원본과 그 안의 구조
프라이머리 샤드 · 샤드 · 샤딩 · 인덱스 · 수평 분할 · 라우팅 · Lucene
레플리카 샤드를 들고 있는 서버 단위
노드 · 데이터 노드 · 마스터 노드 · 클러스터 · 분산 시스템
레플리카 샤드가 기대는 복제 방식
복제 · 프라이머리-백업 복제 · 동기 복제 · 비동기 복제 · 복제 지연
레플리카 샤드가 지키는 성질
고가용성 · 내결함성 · 내구성 · 수평 확장 · 단일 장애점
원본을 잃은 뒤 거치는 처리 단계
장애 조치 · 승격 · 샤드 복구 · 리밸런싱 · 샤드 할당
레플리카 샤드의 배정을 보여 주는 상태
클러스터 헬스 · 미할당 샤드 · 클러스터 상태
샤드마다 복사본을 두는 제품
Elasticsearch · OpenSearch · Apache Solr · MongoDB
레플리카 샤드와 맞세워지는 복제 단위
레플리카 · 레플리카 셋 · 리드 레플리카
다른 이름: replica shard