엘라스틱서치의 데이터 분산 처리 과정

김민규·2023년 7월 27일
0

elasticsearch

목록 보기
6/7
post-thumbnail

쓰기 작업 시 엘라스틱서치 동작과 동시성 제어

쓰기 작업은 3단계로 수행된다.

  • 조정 단계(Coordination Stage)
  • 주 샤드 단계(Primary Stage)
  • 복제 단계(Replica Stage)

조정 단계

  1. 클라이언트로부터 쓰기 작업 요청을 받는다.
  2. 라우팅으로 적절한 주 샤드를 찾아 요청을 넘겨준다.
  3. 주 샤드 단계 작업을 마치면 최초 요청을 수신했던 조정 노드에게 주 샤드 작업 결과 응답을 넘겨준다.
  4. 클라이언트에게 응답을 돌려준다.

주 샤드 단계

주 샤드가 요청을 넘겨 받은 이후 수행하는 작업들을 주 샤드 단계라고 한다.

  1. 작업 요청을 검증한다.
  2. 주 샤드는 로컬에서 쓰기 작업을 수행하고 완료되면 in-sync 복제본에 병렬적으로 요청을 넘긴다.
  3. 모든 복제본이 작업을 성공적으로 수행하고 주 샤드에 응답을 돌려준다.
  4. 주 샤드가 작업 완료 응답을 보낸다.

    in-sync 복제본: 마스터 노드가 관리하는 복제받을 샤드 목록

복제 단계

in-sync 복제본 샤드는 주 샤드에게 받은 요청을 로컬에서 수행하고 주 샤드에게 작업이 완료됐음을 보고한다.

조정 단계, 주 샤드 단계, 복제 단계는 이 순서대로 실행되지만 종료는 역순이다.



낙관적 동시성 제어(Optimistic Concurrency Control)

한 문서의 views 필드 값을 1로 색인했다면 잠시 후 복제본에도 이 내용이 적용된다.
그런데 이 변경 내용이 복제본 샤드에 완전히 적용되기 전에 다른 클라이언트로부터 주 샤드에 같은 문서의 views 필드 값을 2로 색인하는 변경이 발생했다고 가정해 보자.
  • 분산시스템 특성상 두 요청 중 어떤 요청이 먼저 복제본 샤드로 들어올지는 보장할 수 없다.
    • 메시지 순서의 역전이 일어날 수 있다
  • views를 2로 색인하는 요청이 먼저 들어온 뒤에 views를 1로 색인하는 요청이 나중에 들어온다면 복제본에서는 최종값이 1로 역전될 수도 있다.
  • 이러한 현상을 막기 위해 _seq_no가 존재한다.

_seq_no

_seq_no는 각 주 샤드마다 들고 있는 시퀀스 숫자값이며 매 작업마다 1씩 증가한다.

  • 엘라스틱서치는 문서를 색인할 때 이 값을 함께 저장한다.
  • 문서가 색인된 상태에서 _id 값은 같은데 _seq_no 값은 더 작은 색인 요청을 받는다면 이전 버전의 요청을 늦게 들어온 것으로 판단하고 작업을 수행하지 않는다.

_primary_term

주 샤드를 들고 있는 노드에 문제가 발생하여 해당 노드가 클러스터에서 빠진다면 엘라스틱서치는 복제본 샤드 중 하나를 주 샤드로 지정한다.

  • _primary_term 이라는 값으로 새로 임명된 주 샤드를 구분한다.
  • 주 샤드가 새로 지정될 때 1씩 증가한다.
PUT concurrency_test
{
    "settings": {
      	"number_of_shards": 2
    }
}
PUT concurrency_test/_doc/1
{
  	"views": 1
}
{
    "_index": "concurrency_test",
    "_type": "_doc",
    "_id": "1",
    "_version": 1,
    "result": "created",
    // ...
    "_seq_no": 0,
    "_primary_term": 1
}

같은 _id로 문서를 색인하면 _seq_no가 올라간다.

PUT concurrency_test/_doc/1
{
  	"views": 2
}
{
    "_index": "concurrency_test",
    "_type": "_doc",
    "_id": "1",
    "_version": 2,
    "result": "updated",
    // ...
    "_seq_no": 1,
    "_primary_term": 1
}

_seq_no는 주 샤드마다 따로 매긴다.

PUT concurrency_test/_doc/A
{
  	"views": 0
}
{
  	"_index": "concurrency_test",
  	"_type": "_doc",
	"_id": "A",
  	"_version": 1,
  	"result": "created",
  	// ...
  	"_seq_no": 0,
  	"_primary_term": 1
}
  • 문서에 색인할 때 if_seq_noif_primary_term에 각각 _seq_no_primary_term을 넣을 수 있다.
  • 이 경우 문서의 _seq_no_primary_term이 지정한 값과 같을 때만 색인 작업이 수행된다.
  • 클라이언트가 자신이 데이터를 확인했을 때와 완전히 동일한 상태인 경우에만 작업을 수행하고 싶을 때 이용한다.
PUT concurrency_test/_doc/1?if_primary_term=1&if_seq_no=1
{
	"views": 0
}

버전

  • _version_seq_no_primary_term처럼 동시성을 제어하기 위한 메타데이터로 모든 문서마다 붙는다.
  • _version은 0이상 long 범위 이내의 정수여야 한다.
    • 1부터 시작해서 업데이트나 삭제 작업을 수행할 때마다 1씩 증가한다.
  • _seq_no, _primary_term과 다른 점은 클라이언트가 문서의 _version 값을 직접 지정할 수 있다.
    • 현재의 버전값보다 낮은 값으로 색인할 수 없다.
    • version_typeexternal(external_gt)이나 external_gte로 설정하면 클라이언트가 직접 _version을 지정할 수 있다.
      • default version_typeinternal

다른 스토리지에서 저장된 데이터의 버전을 따로 관리하고 있고 그 데이터를 엘라스틱서치로 받아 와서 2차 스토리지로 동기화하여 사용하는 경우 활용하기 좋다.



읽기 작업 시 엘라스틱서치 동작

  1. 조정 노드(Coordination Node)는 클라이언트로부터 읽기 작업을 요청받는다.
  2. 조정 노드는 라우팅을 통해 적절한 샤드를 찾아 요청을 넘긴다.
    • 쓰기 작업과 다르게 주 샤드가 아니라 복제본 샤드로 요청이 넘어갈 수 있다.
  3. 요청을 넘겨받은 각 샤드는 로컬에서 읽기 작업을 수행한 뒤 그 결과를 조정 노드로 돌려준다.
  4. 조정 노드는 이 결과를 모아서 클라이언트에게 응답한다.
🚨 주 샤드에 색인이 완료됐지만 특정 복제본 샤드에는 반영이 완료되지 않은 상태의 데이터를 읽을 수도 있다.

체크포인트와 샤드 복구 과정

만약 문제가 생겨서 특정 노드가 재기동되었다면 그 노드가 들고 있던 샤드에 복구 작업이 진행된다.

  • 복구중인 샤드가 현재 주 샤드의 내용과 일치하는지를 파악할 필요가 있다.
  • 주 샤드가 색인 작업이 있었다면 그 내용을 복제본 샤드에도 반영해야 한다.

_primary_term_seq_no를 조합하면 샤드와 샤드 사이에 어떤 반영 차이점이 있는지를 알 수 있다.

각 샤드는 로컬에 작업을 수행하고나면 몇 번 작업까지 순차적으로 수행을 완료했는지를 ✅ 로컬 체크포인트로 기록한다.

ex) _seq_no가 각각 1, 2, 3, 5, 7인 작업을 수행한 샤드는 로컬 체크포인트값을 3으로 업데이트한다.
  - 3번 작업까지는 빠짐없이 반영됐다는 뜻이다.
  - 복제본 샤드는 로컬 체크포인트가 업데이트되면 이를 주 샤드에 보고한다.
  - 이후에 이 복제본 샤드가 4번 작업을 수신하여 반영을 완료하면 로컬 체크포인트를 5로 업데이트한다.

주 샤드는 각 복제본 샤드로부터 받은 로컬 체크포인트를 비교하여 가장 낮은 값을 ✅ 글로벌 체크포인트 값으로 기록한다.

  • 해당 작업까지는 그 내용이 모든 샤드에 반영됐다는 것을 나타낸다.

글로벌 체크포인트가 업데이트되면 다음 색인 작업 시 그 내용을 주 샤드가 복제본 샤드로 전달한다.

문제가 발생해 샤드를 복구해야 할 경우가 생기면 샤드 간에 글로벌 체크포인트를 비교한다.

  • 주 샤드와 복제본 샤드의 글로벌 체크포인트가 같다면 이 샤드는 추가 복구 작업이 필요 없다.
  • 글로벌 체크포인트가 차이 난다면 두 샤드 간 체크포인트를 확인해서 필요한 작업만 재처리하여 복구한다.
    • 세그먼트 파일을 통째로 전송하는 것보다 효율적이다.

샤드 이력 보존(Shard History Retention Leases)

엘라스틱서치는 논리적 삭제를 도입했다.
최근 삭제한 문서를 일정 기간 보존해 두고 작업 재처리에 활용한다.

재처리할 내용을 추적하는 메커니즘
루씬의 세그먼트가 병합되는 도중에도 샤드 이력은 지정한 기간 동안은 보존된다. (default: 12시간)

index.soft_deletes.retention_lease.period

참조

profile
Backend Engineer, Vim User

0개의 댓글