Elastic Search

Ch09. 엘라스틱서치와 루씬 이야기- 샤드 최적화

webmaster 2025. 10. 9. 11:51
728x90

운영 중에 샤드의 개수를 수정하지 못하는 이유

클러스터에서 운영 중인 인덱스의 샤드 개수는 원칙적으로 수정이 불가능하다. 인덱스를 생성할 때 한번 설정된 샤드의 개수는 절대 변경이 불가능하기 때문에 데이터의 크기가 최대 얼마까지 증가할 것인지를 사전에 잘 계산하여 최초 인덱스를 생성할 때 샤드의 개수를 신중하게 결정해야 한다.

 

샤드의 종류

  • 프라이머리 샤드
    • 실제 서비스가 일어나는 샤드로 클러스터에서 실질적인 CRUD를 제공하는 샤드이다.
  • 레플리카 샤드
    • 장애 복구를 위한 샤드로, 프라이머리 샤드와 동일한 데이터를 가지고 있으며 평상시에는 읽기 분산에 사용된다.

엘라스틱서치는 최초 인덱스를 생성할 때 settings 속성을 통해 프라이머리 샤드 개수(number_of_shards)와 레플리카 샤드 개수(number_of_replicas)를 설정할 수 있다.

PUT /movie
{
  "settings": {
    "index": {
      "number_of_shards": 5,
      "number_of_replicas": 1
    }
  }
}

프라이머리 샤드와 레플리카 샤드

  • number_of_shards: 프라이머리 샤드를 몇 개로 나눌지 설정하는 속성으로 해당 값에 의해 프라이머리 샤드의 개수가 결정
  • number_of_replicas: 레플리카 세트를 몇 개로 구성할지 설정하는 속성으로 해당 값에 의해 레플리카 샤드의 개수가 결정

엘라스틱서치가 프라이머리 샤드의 개수 변경을 허용하지 않는 이유는 샤드는 루씬 인덱스의 확장이고, 각 샤드의 내부에 독립적인 루씬 라이브러리를 가지고 있어, 해당 루씬이 단일 머신 위에서만 동작하는 검색엔진이기 때문이다. 즉, 각 인덱스를 구성하는 전체 데이터가 별도로 존재하고 자신은 그중 일부만 가지고 있는 것을 알 수 없이 일부 데이터만 색인과 검색을 효과적으로 하기 위해 노력하기 때문이다.

프라이머리 샤드의 개수를 변경한다는 것은 독립적인 루씬이 가진 데이터를 모두 재조정한다는 의미로 루씬 내부에 가지고 있는 세그먼트들이 잘게 쪼개져 새로 추가된 루씬 쪽으로 이동해야 한다는 의미인데, 이는 세그먼트의 불변성으로 인해 원칙적으로 불가능하다(프라이머리 샤드 개수는 변경이 불가능하다)

 

프라이머리 샤드의 개수를 변경해야 할 경우 새로운 인덱스를 생성 후 재색인 하도록 가이드하며, ReIndex API를 제공한다.

POST _reindex
{
  "source": {
    "index": "movie",
    "query": {
      "term": { "genre": "action" }
    }
  },
  "dest": {
    "index": "new_movie"
  }
}

 

레플리카 샤드의 복제본 수는 얼마가 적당할까?

  • 레플리카 샤드의 복제 수는 운영 중에도 얼마든지 변경이 가능하다.
    • 기존 프라이머리 샤드를 단순 복사만 하면 되기 때문이다.
  • 일반적으로 장애가 발생했을때 빠른 복구를 위해 1개 이상의 복제본 세트를 사용하는 것이 좋다.
    • 레플리카 샤드는 직접 생성할 샤드의 개수를 지정할 수는 없고 몇 개의 복사본을 가질 것인지만 설정 가능하다.
    • 레플리카 샤드가 프라이머리 샤드 전체를 복사해야 의미가 있기 때문이다.
  • 복제본 수를 지정함으로 모든 프라이머리 샤드가 복제되기 때문에 클러스터 내부에 운영하고 있는 전체 샤드 개수를 고려해서 적절한 레플리카 샤드를 운영해야 한다.
    • 레플리카 세트의 수를 결정하기 위해서는 사전에 테스트가 필요하며, 너무 많은 복제본 생성 시 색인 성능이 저하된다.
    • 레플리카 샤드도 내부에 루씬을 가지고 있고, 데이터가 추가되면 레플리카 샤드 또한 동일한 검색 결과를 보장해야 하므로 동일하게 데이터가 전송이 되며 세그먼트 생성 과정을 거쳐야 한다.(일관성 유지)
    • 따라서, 레플리카가 많아지면 색인 성능은 떨어질 수밖에 없어 읽기 성능이 중요하면 레플리카 수를 늘리고 빠른 색인이 중요한 경우 레플리카 수를 줄이는 것이 좋다.
  • 복제본 수는 운영 언제나 수정 가능하므로 서비스 오픈 시점에는 복제본 수를 최소화하여 운영하는 것이 좋다
    • 추후 지속적인 모니터링을 통해 복제본 수를 늘리거나, 줄이는 것을 추천한다.

클러스터에서 운영 가능한 최대 샤드 수는?

PUT /test
"settings" : {
  "index" : {
    "number_of_shards" : 1025,
    "number_of_replicas" : 1
  }
}

응답

{
  "error": {
    "root_cause": [
      {
        "type": "illegal_argument_exception",
        "reason": "Failed to parse value [1025] for setting [index.number_of_shards] must be <= 1024"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "Failed to parse value [1025] for setting [index.number_of_shards] must be <= 1024"
  },
  "status": 400
}
  • 인덱스를 생성할 때 기본값으로 5개의 프라이머리 샤드와 1개의 레플리카 복제본을 생성하기에 하나의 인덱스 생성 시 최소 10개의 샤드가 클러스터 내부에 생성된다.
  • 엘라스틱서치 클러스터에서 운영 가능한 전체 샤드의 개수 제한은 없으나, 개별 인덱스를 생성할 때 설정 가능한 샤드의 수는 현재 1024로 제한돼 있다.

클러스터에 많은 수의 샤드가 존재할 경우

  • 클러스터에 존재하는 모든 샤드는 마스터 노드에 의해 관리되며, 샤드가 많아질수록 마스터 노드의 부하도 증가된다.
  • 마스터 노드가 처리해야 하는 정보가 많아지면 검색/색인 작업도 느려지며, 마스터 노드의 메모리 사용량도 늘어나게 된다.
    • 마스터 노드가 빠른 처리를 위해 샤드 정보와 같은 관리 데이터를 모두 메모리에 올려서 제공하기 때문
참고 
마스터 노드 역할
1) 모든 노드와 샤드를 관리하는 책임
2) 평소 노드의 상태를 모니터링하며, 색인 요청에 대한 데이터의 라우팅을 처리하거나 검색 요청에 대한 부하를 분산하는 역할
3) 장애 발생 시 레플리카를 이용해 샤드를 복구하는 책임

인덱스가 다수의 샤드로 분산될 경우

  • 샤드가 여러 개로 나눠져 있을 경우 다수의 머신 파워를 동시에 사용할 수 있게 되고, 그에 비례하여 검색 시간이 단축되기 때문에 클러스터의 전체 읽기 성능이 좋아진다.

샤드의 물리적인 크기와 복구 시간

  • 샤드가 가지고 있는 데이터 건수보다 데이터의 물리적인 크기가 더 중요하다
    • 마스터 노드는 장애 발생 시, 샤드 단위로 복구를 수행하기 때문
  • 장애 발생 시, 레플리카 샤드가 순간적으로 프라이머리 샤드로 전환되며, 프라이머리 샤드로 전환된 샤드와 동일한 샤드가 물리적으로 다른 장비에서 레플리카 샤드로 새롭게 생성된다.
    • 시간이 지나 장애가 복구되면, 복구된 노드로 일부 샤드들이 네트워크를 통해 이동하며, 내부적으로 전체적인 클러스터 균형을 맞춘다.
  • 복구 시 샤드 단위로 데이터가 이동하기 때문에 샤드의 크기가 클수록 복구 작업이 오래 걸린다.

적절한 샤드의 크기

  • 엘라스틱 서치에서는 샤드 1개가 물리적으로 50GB를 넘지 않는 것을 권장한다.
  • 너무 큰 샤드의 크기를 가진다면(400GB)
    • 2개 샤드로 분산 저장될 경우 -> 하나의 샤드가 200GB를 가지고 복구를 위한 네트워크 비용이 너무 크다.
    • 400개의 샤드로 분산 저장될 경우 -> 하나의 샤드가 1GB의 크기를 가지고, 복구 비용은 작으나 마스터 노드의 부하와 많은 리소스 낭비를 가져오게 된다.
    • 샤드의 개수가 8개로 설정하는 것이 가장 적절하며, 미래에 추가될 데이터까지 감안한다면 이를 포함해 샤드의 수를 적절히 계산해야 한다.
  • 인덱스가 작은 데이터를 가지는 경우(1GB)
    • 5개의 샤드로 분산 저장될 경우 -> 인덱스의 샤드가 기본 설정(5개)이면 충분할 것이다
    • 100개의 샤드로 분산 저장될 경우 -> 하나의 샤드가 10MB 크기를 가질 것이다. 심한 리소스 낭비를 초래한다.
    • 샤드의 개수는 기본 설정으로 제공하면 적절하다

실제 데이터와 다양한 검색조건을 사용해 충분히 테스트를 진행해서 샤드의 수를 결정하자

하나의 인덱스에 생성 가능한 최대 문서 수는?

  • number_of_shards 옵션에 의해 인덱스당 생성할 수 있는 최대 샤드의 개수가 1024개 인 것은 최근에 생긴 제약으로, 너무 많은 샤드를 생성했을 때 마스터 노드에 걸리는 부하를 보호하기 위해 최소한의 제약을 만든 것이다.
  • 샤드는 내부적으로 루씬 인덱스를 확장하므로 다수의 세그먼트를 가질 수 있고, 지속적인 merge 작업을 통해 하나의 커다란 세그먼트로 통합이 되는데, 세그먼트는 최대 Integer.MAX_VALUE -128개를 뺀 대략 20억 개 정도이다.
  • 하나의 인덱스는 최대 1024개 샤드를, 각 샤드는 20억 개의 세그먼트를 가질 수 있기 때문에 최대 문서의 수는 약 2조 개다.
    • 이는 이론적인 수치로 데이터의 물리적 크기와 하드웨어 리소스를 기반으로 적절한 문서 수를 유지하는 게 좋다.

엘라스틱서치는 분산 검색엔진으로 설계됐기 때문에 색인 가능한 문서 수의 제약을 제거했다. 안정적인 클러스터 운영을 위해서는 자신의 환경에 맞게 다방면에 걸친 충분한 테스트가 필요하다.

728x90