paint-brush
Elasticsearch에서 문서 업데이트를 위한 간단한 가이드~에 의해@rocksetcloud
6,621 판독값
6,621 판독값

Elasticsearch에서 문서 업데이트를 위한 간단한 가이드

~에 의해 Rockset9m2024/04/12
Read on Terminal Reader

너무 오래; 읽다

이 블로그에서는 검색 및 분석 애플리케이션에 필수적인 Elasticsearch의 업데이트 처리를 위한 필수 전략을 살펴봅니다. CPU 사용률에 미치는 영향과 함께 전체 업데이트, 부분 업데이트, 스크립트 업데이트에 대해 알아보세요. 빈번한 문서 수정을 효율적으로 관리하기 위해 Rockset과 같은 대안을 살펴보세요.
featured image - Elasticsearch에서 문서 업데이트를 위한 간단한 가이드
Rockset HackerNoon profile picture
0-item
1-item



Elasticsearch 는 Apache Lucene을 기반으로 하는 오픈 소스 검색 및 분석 엔진입니다. Elasticsearch를 사용하여 변경 데이터 캡처(CDC) 데이터에 대한 애플리케이션을 구축할 때 인덱스의 기존 문서에 대한 빈번한 업데이트 또는 수정을 처리하도록 시스템을 설계하는 것이 좋습니다.


이 블로그에서는 전체 업데이트, 부분 업데이트, 스크립트 업데이트를 포함하여 업데이트에 사용할 수 있는 다양한 옵션을 살펴보겠습니다. 또한 문서를 수정할 때 Elasticsearch 내부에서 어떤 일이 일어나는지, 그리고 업데이트 빈도가 시스템의 CPU 사용률에 얼마나 영향을 미치는지에 대해서도 논의하겠습니다.

자주 업데이트되는 애플리케이션 예시

자주 업데이트 되는 사용 사례를 더 잘 이해하기 위해 Netflix와 같은 비디오 스트리밍 서비스에 대한 검색 애플리케이션을 살펴보겠습니다. 사용자가 '정치 스릴러'와 같은 프로그램을 검색하면 키워드 및 기타 메타데이터를 기반으로 관련 결과 집합이 반환됩니다.


"House of Cards" 쇼에 대한 Elasticsearch의 예제 문서를 살펴보겠습니다.

 { "name": "House of Cards", "description": "Frank Underwood is a Democrat appointed as the Secretary of State. Along with his wife, he sets out on a quest to seek revenge from the people who betrayed him while successfully rising to supremacy.", "genres": ["drama", "thriller"], "views": 100, }


namedescription 전체 텍스트 검색 필드로 사용하도록 Elasticsearch에서 검색을 구성할 수 있습니다. 타이틀당 조회수를 저장하는 views 필드는 콘텐츠를 늘리는 데 사용될 수 있으며 인기 있는 프로그램의 순위를 더 높게 지정할 수 있습니다. views 필드는 사용자가 프로그램이나 영화의 에피소드를 시청할 때마다 증가합니다.


Netflix 규모의 애플리케이션에서 이 검색 구성을 사용하면 수행되는 업데이트 수가 Netflix 참여 보고서 에 따라 분당 수백만 개를 쉽게 넘을 수 있습니다. 보고서에 따르면 사용자들은 1월부터 7월까지 약 1,000억 시간의 콘텐츠를 시청했습니다. 회나 영화당 평균 시청시간을 15분으로 가정하면 분당 조회수는 평균 130만 회에 이른다. 위에 지정된 검색 구성을 사용하면 각 보기에 수백만 단위의 업데이트가 필요합니다.


많은 검색 및 분석 애플리케이션은 특히 CDC 데이터를 기반으로 구축된 경우 자주 업데이트될 수 있습니다.

Elasticsearch에서 업데이트 수행

아래 코드를 사용하여 Elasticsearch에서 업데이트를 수행하는 방법에 대한 일반적인 예를 살펴보겠습니다.

 - from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID you want to update index_name = 'movies' document_id = 'your_document_id' # Retrieve the current document to get the current 'views' value try: current_doc = es.get(index=index_name, id=document_id) current_views = current_doc['_source']['views'] except Exception as e: print(f"Error retrieving current document: {e}") current_views = 0 # Set a default value if there's an error # Define the update body to increment 'views' by 1 update_body = { "doc": { "views": current_views + 1 # Increment 'views' by 1 } } # Perform the update try: es.update(index=index_name, id=document_id, body=update_body) print("Document updated successfully!") except Exception as e: print(f"Error updating document: {e}")


Elasticsearch의 전체 업데이트와 부분 업데이트 비교

Elasticsearch에서 업데이트를 수행할 때 인덱스 API를 사용하여 기존 문서를 바꾸거나 업데이트 API를 사용 하여 문서를 부분적으로 업데이트할 수 있습니다.


인덱스 API는 전체 문서를 검색하고 문서를 변경한 다음 문서를 다시 인덱싱합니다. 업데이트 API를 사용하면 전체 문서 대신 수정하려는 필드만 보낼 수 있습니다. 이렇게 해도 문서가 다시 색인화되지만 네트워크를 통해 전송되는 데이터의 양은 최소화됩니다. 업데이트 API는 문서 크기가 크고 네트워크를 통해 전체 문서를 보내는 데 시간이 많이 걸리는 경우에 특히 유용합니다.


Python 코드를 사용하여 인덱스 API와 업데이트 API가 어떻게 작동하는지 살펴보겠습니다.

Elasticsearch의 인덱스 API를 사용한 전체 업데이트

 from elasticsearch import Elasticsearch # Connect to Elasticsearch es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID index_name = "your_index" document_id = "1" # Retrieve the existing document existing_document = es.get(index=index_name, id=document_id) # Make your changes to the document existing_document["_source"]["field1"] = "new_value1" existing_document["_source"]["field2"] = "new_value2" # Call the index API to perform the full update es.index(index=index_name, id=document_id, body=existing_document["_source"])


위 코드에서 볼 수 있듯이 인덱스 API에는 Elasticsearch에 대한 두 번의 별도 호출이 필요하며 이로 인해 성능이 저하되고 클러스터의 로드가 높아질 수 있습니다.


Elasticsearch의 업데이트 API를 사용한 부분 업데이트

부분 업데이트는 내부적으로 reindex API를 사용하지만 더 나은 성능을 위해 단일 네트워크 호출만 필요하도록 구성되었습니다.


 from elasticsearch import Elasticsearch # Connect to Elasticsearch es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID index_name = "your_index" document_id = "1" # Specify the fields to be updated update_fields = { "field1": "new_value1", "field2": "new_value2" } # Use the update API to perform a partial update es.update(index=index_name, id=document_id, body={"doc": update_fields})


Elasticsearch의 업데이트 API를 사용하여 조회수를 업데이트할 수 있지만 업데이트 API 자체를 사용하여 이전 값을 기반으로 조회수를 늘릴 수는 없습니다. 새로운 조회수 값을 설정하려면 이전 조회수가 필요하기 때문입니다.


강력한 스크립트 언어인 Painless를 사용하여 이 문제를 어떻게 해결할 수 있는지 살펴보겠습니다.


Elasticsearch에서 Painless 스크립트를 사용한 부분 업데이트

Painless 는 Elasticsearch용으로 설계된 스크립팅 언어이며 쿼리 및 집계 계산, 복잡한 조건, 데이터 변환 등에 사용할 수 있습니다. Painless에서는 업데이트 쿼리에 스크립트를 사용하여 복잡한 논리를 기반으로 문서를 수정할 수도 있습니다.


아래 예에서는 Painless 스크립트를 사용하여 단일 API 호출로 업데이트를 수행하고 이전 조회수 값을 기준으로 새 조회수를 늘립니다.


 from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID you want to update index_name = 'movies' document_id = 'your_document_id' # Define the Painless script for the update update_script = { "script": { "lang": "painless", "source": "ctx._source.views += 1" # Increment 'views' by 1 } } # Perform the update using the Painless script try: es.update(index=index_name, id=document_id, body=update_script) print("Document updated successfully!") except Exception as e: print(f"Error updating document: {e}")


Painless 스크립트는 이해하기 매우 직관적입니다. 단순히 모든 문서에 대해 조회수를 1씩 늘리는 것뿐입니다.


Elasticsearch에서 중첩 객체 업데이트

Elasticsearch의 중첩 개체는 단일 상위 문서 내에서 별도의 문서로 개체 배열의 인덱싱을 허용하는 데이터 구조입니다. 중첩된 개체는 개체 내의 개체처럼 자연스럽게 중첩된 구조를 형성하는 복잡한 데이터를 처리할 때 유용합니다. 일반적인 Elasticsearch 문서에서는 객체 배열이 평면화되지만 중첩된 데이터 유형을 사용하면 배열의 각 객체를 독립적으로 인덱싱하고 쿼리할 수 있습니다.


무통 스크립트를 사용하여 Elasticsearch의 중첩 개체를 업데이트할 수도 있습니다.

 from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID for the example index_name = 'your_index' document_id = 'your_document_id' # Specify the nested field and the updated value nested_field = "nested_field_name" updated_value = "new_value" # Define the Painless script for the update update_script = { "script": { "lang": "painless", "source": "ctx._source.nested_field_name = params.updated_value", "params": { "updated_value": updated_value } } } # Perform the update using the Update API and the Painless script try: es.update(index=index_name, id=document_id, body=update_script) print("Nested object updated successfully!") except Exception as e: print(f"Error updating nested object: {e}")


Elasticsearch에 새 필드 추가

Elasticsearch의 문서에 새 필드를 추가하는 것은 인덱스 작업을 통해 수행할 수 있습니다.


업데이트 API를 사용하면 기존 문서를 새 필드로 부분적으로 업데이트할 수 있습니다. 인덱스에 대한 동적 매핑이 활성화되면 새 필드를 도입하는 것이 간단합니다. 해당 필드가 포함된 문서를 색인화하기만 하면 Elasticsearch가 자동으로 적합한 매핑을 찾아 매핑에 새 필드를 추가합니다.


인덱스에 대한 동적 매핑이 비활성화되면 업데이트 매핑 API를 사용해야 합니다. 아래에서 영화 인덱스에 "범주" 필드를 추가하여 인덱스 매핑을 업데이트하는 방법에 대한 예를 볼 수 있습니다.


 PUT /movies/_mapping { "properties": { "category": { "type": "keyword" } } }


Elasticsearch 내부 업데이트

코드는 간단하지만 데이터가 불변 세그먼트에 저장되기 때문에 Elasticsearch는 내부적으로 이러한 업데이트를 수행하기 위해 많은 노력을 기울이고 있습니다. 결과적으로 Elasticsearch는 단순히 문서를 내부 업데이트할 수 없습니다. 업데이트를 수행하는 유일한 방법은 사용되는 API에 관계없이 전체 문서를 다시 색인화하는 것입니다.


Elasticsearch는 내부적으로 Apache Lucene을 사용합니다. Lucene 인덱스는 하나 이상의 세그먼트로 구성됩니다. 세그먼트는 전체 인덱스의 하위 집합을 나타내는 독립적이고 변경할 수 없는 인덱스 구조입니다. 문서가 추가되거나 업데이트되면 새 Lucene 세그먼트가 생성되고 이전 문서는 일시 삭제 대상으로 표시됩니다. 시간이 지나면서 새 문서가 추가되거나 기존 문서가 업데이트되면서 여러 세그먼트가 누적될 수 있습니다. 인덱스 구조를 최적화하기 위해 Lucene은 주기적으로 작은 세그먼트를 더 큰 세그먼트로 병합합니다.

업데이트는 기본적으로 Elasticsearch의 삽입입니다.

각 업데이트 작업은 재색인 작업이므로 모든 업데이트는 기본적으로 일시 삭제가 포함된 삽입입니다.


업데이트를 삽입 작업으로 처리하면 비용에 영향을 미칩니다. 한편, 데이터의 소프트 삭제는 오래된 데이터가 일정 기간 동안 계속 유지되어 인덱스의 저장 공간과 메모리가 팽창한다는 것을 의미합니다. 일시 삭제, 재인덱싱 및 가비지 수집 작업을 수행하는 경우에도 CPU에 막대한 비용이 소요되며, 모든 복제본에서 이러한 작업을 반복하면 비용이 더욱 악화됩니다.


제품이 성장하고 시간이 지남에 따라 데이터가 변경됨에 따라 업데이트가 더욱 까다로워질 수 있습니다. Elasticsearch 성능을 유지하려면 클러스터의 샤드, 분석기 및 토크나이저를 업데이트해야 하며 전체 클러스터를 다시 인덱싱해야 합니다. 프로덕션 애플리케이션의 경우 새 클러스터를 설정하고 모든 데이터를 마이그레이션해야 합니다. 클러스터 마이그레이션은 시간이 많이 걸리고 오류가 발생하기 쉬우므로 가볍게 수행할 작업이 아닙니다.

Elasticsearch의 업데이트

Elasticsearch의 업데이트 작업이 단순하기 때문에 시스템 내부에서 발생하는 과도한 작업이 가려질 수 있습니다. Elasticsearch는 각 업데이트를 삽입으로 처리하므로 전체 문서를 다시 생성하고 다시 색인화해야 합니다. 업데이트가 자주 발생하는 애플리케이션의 경우 매분 수백만 건의 업데이트가 발생하는 Netflix 예에서 볼 수 있듯이 비용이 빠르게 높아질 수 있습니다. 워크로드에 대기 시간을 추가하는 대량 API를 사용하여 업데이트를 일괄 처리하거나 Elasticsearch에서 자주 업데이트되는 경우 대체 솔루션을 살펴보는 것이 좋습니다.


클라우드에 구축된 검색 및 분석 데이터베이스인 Rockset은 Elasticsearch의 변경 가능한 대안입니다. 가변성으로 인해 대중화된 키-값 저장소인 RocksDB를 기반으로 구축된 Rockset은 문서를 내부 업데이트할 수 있습니다. 이로 인해 전체 문서가 아닌 개별 필드의 값만 업데이트되고 다시 색인화됩니다.


업데이트가 많은 워크로드에 대해 Elasticsearch와 Rockset의 성능을 비교하고 싶다면 $300 크레딧으로 Rockset 무료 평가판을 시작할 수 있습니다.