Elasticsearch에서는 종종 몇 개의 필드만 검색해야 하는 경우가 많다. 이런 경우, 해당 필드들에 대해 `doc_values` 를 사용하면 쿼리 성능을 크게 향상시킬 수 있다.
이때, 기본적으로 검색되는 하나의 필드가 바로 `_id` 필드이다. 겉으로 보기에 이 필드는 아무 문제가 없어보이지만, 시스템 성능을 저해할 수 있다.
또 다른 전형적인 Elasticsearch 사용 사례는 보조 데이터베이스로서의 역할, 특히 전문 검색에서 관련 문서의 ID를 검색하는 데 Elasticsearch를 사용하는 경우이다.

대부분의 Elasticsearch 를 사용하는 사용자는 외부 ID를 `_id` 필드에 저장하여 일을 간단하게 하고 데이터 중복을 피하는 경우가 많다. 혹은 `_id`를 직접 사용하지 않더라도 기본적으로 반환되기 때문에 간접적으로 사용하게 되는 경우도 있다.
이 경우의 문제점은 `_id` 가 `stored_fields` 를 사용하여 저장되며, 이는 `doc_values` 에 비해 읽기 오버헤드가 더 클 수 있다는 것이다. 특히 Elasticsearch 7.10+에서는 `stored_fields` 가 80KB 블록으로 쓰기/읽기 시작했다(이전에는 16KB였음). 이는 Lucene(Elasicsearch의 기반 검색 엔진)에서 문서 압축을 최적화하기 위해 수행된 최적화 작업이며, 특히 작은 문서에 대해 효과적이다.
읽기 성능 향상시키기
Elasticsearch 읽기 성능을 향상시키는 방법은 간단하다. `doc_values` 를 사용하고, (_id 필드를 포함하는) `stored_fields` 의 검색을 비활성화하면 된다.
즉, 다음과 같은 쿼리를:
GET test/_search
{
"query": {
"match_all": {}
},
"_source": false
}
다음과 같이 변환한다:
GET test/_search
{
"query": {
"match_all": {}
},
"_source": false,
"stored_fields": "_none_",
"docvalue_fields": ["my_id_field"]
}
- "stored_fields": "_none_": 이 설정은 _source와 _id와 같은 모든 stored_fields의 검색을 비활성화한다.
- "docvalue_fields": ["my_id_field"]: 이를 통해 선택한 doc_values 필드만 검색할 수 있다.
이러한 변경을 통해, Elasticsearch에서 필요한 필드만 효율적으로 검색하여 시스템의 전반적인 읽기 성능을 크게 향상시킬 수 있다. 특히, `_id` 필드와 같이 기본적으로 검색되는 필드의 검색을 비활성화함으로써, 불필요한 데이터 검색으로 인한 오버헤드를 줄이고, 필요한 정보만을 빠르게 검색하는 것이 가능해진다.
벤치마크 측정
- 인덱스 크기: 130만 개 문서
- 문서 크기: 약 ~4KB
- 쿼리: 랜덤 매치 조건을 가진 10,000개의 쿼리

결과는 예상대로 비교적 작은 문서 크기에도 불구하고, 결과에서 `_id` 를 제외하고 오직 `doc_values` 필드만 검색하는 것이 지연 시간을 크게 개선한다는 것을 볼 수 있다. 문서의 크기가 커질 수록 격차는 더 심해진다.
이 데이터는 Elasticsearch에서 _id 필드의 사용을 중단하고 doc_values를 활용할 때 읽기 성능이 얼마나 향상될 수 있는지를 명확하게 보여준다. 특히, 대량의 데이터를 빠르게 처리해야 하는 애플리케이션에서는 이러한 최적화가 검색 속도와 시스템 반응성을 크게 개선할 수 있다. 따라서, 성능 중심의 Elasticsearch 환경을 구축하고자 하는 개발자와 시스템 관리자에게 중요한 참고 자료가 될 것이다.
Lucene 내부에 대한 심층 분석
`_id` 를 버리고 `doc_values` 를 사용하여 필드를 검색하는 것이 더 효율적일 수 있다는 것은 위에 내용을 통해 보였다.
좀더 내부를 깊이 있게 살펴보면 다음과 같다.
Stored Fields
저장된 필드(Stored Fields)의 경우, 각 문서는 연속적으로 모든 저장된 필드를 포함하는 행으로 저장된다. 첫 번째 필드는 `_id` 이며, 그 다음은 다른 개별 저장된 필드들이고, 마지막 저장된 필드는 `_source` 이다.
이 구조는 Elasticsearch에서 데이터를 관리하고 검색하는 방식에 중요한 역할을 하며, 성능 최적화와 관련된 결정을 내릴 때 고려해야 할 핵심 요소 중 하나이다.

앞서 설명한 Lucene에서 블록이 저장되는 방식을 단순화하여 설명했다. 실제로는, 이러한 블록들이 공유 사전을 가진 10*8 KB의 서브 블록으로 나뉜다.
이 방식의 이점은 두 가지다. 작은 문서들이 각 블록 내에 여러 개 존재할 경우 더 나은 압축 효과를 얻을 수 있다.
읽기 작업에 있어서는, 개별 서브 블록을 압축 해제할 수 있으므로, 특정 필드/문서를 검색하기 위해 한 서브 블록만 압축 해제할 수도 있다.
값을 검색하기 위해서, Lucene은 먼저 필드 인덱스(.fdx) 파일을 읽어야 한다. 이 파일은 두 개의 단조롭게 증가하는 배열(오름차순)을 저장하는데, 하나는 압축된 문서의 각 블록에 대한 첫 번째 Doc ID용이고, 다른 하나는 디스크 상의 해당 오프셋용이다. Doc ID를 포함하는 배열은 예상되는 Doc ID를 포함하는 블록을 찾기 위해 이진 검색되며, 두 번째 배열에서 디스크 상의 관련 오프셋이 검색된다.
이로 인해 검색해야 할 각 저장된 필드 값에 대해 두 번의 디스크 탐색이 발생할 수 있다.
이 Doc ID들은 Lucene 내부적인 것으로 Elasticsearch의 _id와는 관련이 없다는 점을 주목할 가치가 있다. 이들은 각 Lucene 세그먼트 내에서만 고유하며, 새 문서가 추가될 때마다 증가한다.
DocValues
Lucene은 DocValues 필드의 모든 값을 연속적으로 함께 저장한다. 이 형식은 열 방식(columnar manner)으로도 알려져 있다.
이 값들은 공간 사용을 최적화하는 다양한 형식으로 저장될 수 있으며, 각각 다른 압축 형식을 사용할 수 있는 블록으로 나눌 수 있다.
이들이 docId 순서대로 저장되므로, Lucene은 일치하는 문서의 필드 값들을 검색하기 위해 docId 순서대로 순차적으로 읽기만 하면 된다.
Reference
https://luis-sena.medium.com/stop-using-the-id-field-in-elasticsearch-6fb650d1fbae
Stop using the _id field in Elasticsearch
These simple changes will dramatically improve Elasticsearch's performance.
luis-sena.medium.com
'Elasticsearch' 카테고리의 다른 글
ELK를 활용한 간단한 SIEM 구축해보기 (feat. WhiteHat School) (0) | 2024.01.21 |
---|