Elastic Search

Elasticsearch 공부 정리 - 2

꾸준함. 2021. 7. 22. 17:17

개념 정리

1. index 삭제하는 쿼리

  • DELETE /[인덱스명]

 

2. index 생성하는 쿼리

 

 

3. index 내 document를 입력하는 쿼리와 결과

 

 

* 기존 설정에서 primary shard + two replica shards였다면 total은 3, 즉 total은 replication group와 동일하며 shard 업데이트 성공 횟수는 successful에, 실패 횟수는 failed에 표시

 

4. ID를 기반으로 document 검색하는 쿼리

  • GET [인덱스명]/_doc/[아이디]
  • 결과에서 "found" 필드가 true면 해당 ID를 지닌 document가 존재한다는 의미
  • 결과에서 "found" 필드가 false라면 해당 ID를 지닌 document가 존재하지 않는다는 의미

 

5. ID를 기반으로 document 업데이트하는 쿼리

 

 

6. Document 특성

  • ES 내 document들은 immutable
  • 따라서, document를 업데이트할 때 document를 새로 작성한 뒤 바꿔치기하는 방식으로 진행이 됨
  • ES에서 제공하는 Update API의 내부적인 로직은 위에 언급했던 것처럼 document를 새로 작성한 뒤 기존 document와 바꿔치기하는 방식으로 진행
  • 따라서, 애플리케이션 레벨에서 개발자가 직접 document를 작성하고 기존 document와 교체하는 방식으로 진행해도 되지만 이럴 경우 데이터 생성 + 데이터 교체 총 두 번의 네트워크 통신이 이루어져 비효율적 (Update API는 네트워크 통신이 한번만 이루어짐)
  • 자바의 String을 생각하면 이해하기 편함

 

7. Scripted Update 정리

  • document 값에 접근하는 동안 커스텀 로직을 직접 작성할 수 있도록 지원하는 스크립트
  • ctx는 context의 줄임말이며 ctx를 통해 document 내 source 필드 접근 가능
  • script가 길어지면 쌍따옴표 세 개를 통해 multi line 표시 가능
  • scripted update의 result 값은 항상 update (noop을 발생시키기 위해서는 별도의 조건문을 스크립트 내 작성해야 함)
  • upsert의 경우 document가 이미 있는지 여부에 따라 조건부로 document를 업데이트하거나 입력
  • 따라서, upsert의 result 값은 update 혹은 created

 

 

8. Routing 정리

  • document를 어떤 샤드에 저장할지를 정해주는 프로세스 혹은 document가 위치한 샤드를 찾아주는 프로세스
  • 공식: shard_num = hash(_routing) % num_of_primary_shards (_routing의 디폴트 값은 document 아이디)
  • routing 공식 내 primary 샤드의 개수가 사용되므로 인덱스 생성 후 shard 개수 수정 불가
  • 디폴트 Routing의 경우 document들이 shard들에 고르게 분포될 수 있도록 설계되어 있음
  • Routing은 특정 목적에 따라 커스터마이징도 가능
  • ES는 인덱스 내 document를 저장할 때 source 필드와 함께 메타데이터도 함께 저장하는데 document를 특정 지을 수 있는 _id 필드가 메타데이터 중 하나
  • _routing 필드 또한 메타데이터 중 하나인데 디폴트 Routing을 사용할 때는 해당 필드가 노출되지 않지만 Routing을 커스터마이징 할 경우 _routing 필드가 노출됨

 

9. ES에서 document 읽는 방법

 

10. Primary Terms

  • replication group 내 primary 샤드가 바뀌었을 때 예전 primary 샤드와 새로운 primary 샤드를 구분 지을 수 있게 필요한 개념
  • 기본적으로 primary 샤드의 변경 횟수를 값으로 가짐
  • ES에서 document를 저장할 때 쓰이는 필드

 

11. Sequence Numbers

  • ES에서 document를 저장할 때 primary term과 함께 쓰이는 필드
  • 기본적으로 write operation 즉, document가 저장된 횟수를 값으로 가짐
  • primary 샤드가 저장 요청을 수행할 때마다 sequence number를 1 증가시킴
  • ES의 저장 작업의 순서를 알 수 있게 하는 필드

 

12. Primary 샤드 내 document를 저장할 때 실패할 경우 복구하는 방법

  • 앞서 언급한 primary terms와 sequence numbers 필드가 필요
  • 해당 필드들은 es가 수많은 저장 작업 요청 중 어떤 요청을 다시 수행해야 할지 빠르게 판단할 수 있도록 지원
  • 인덱스 내 document 개수가 많을 경우 이러한 프로세스는 상당히 무거울 수 있기 때문에 es에서는 체크포인트를 이용

 

13. 글로벌, 로컬 체크포인트

  • sequence numbers와 동일한 개념
  • 각 replication group에는 글로벌 체크포인트가 존재
  • 각 replica 샤드에는 로컬 체크포인트가 존재

 

13.1 글로벌 체크포인트

  • replication group 내 활성화된 모든 샤드가 적어도 어느 작업까지는 모두 수행했다는 것을 알려주는 sequence number
  • 즉, 정리하자면 글로벌 체크포인트보다 작은 sequence number를 가진 작업들은 replication group 내에서 모두 수행되었다는 것을 의미
  • 만약 primary 샤드가 다운되고 이후 클러스터 내 복구할 경우, es는 글로벌 체크포인트보다 높은 sequence number를 가진 작업들만 primary 샤드 내에서 수행되었는지 확인하면 됨

 

13.2 로컬 체크포인트

  • 마지막으로 수행된 작업의 sequence number
  • replica 샤드가 다운될 경우, 로컬 체크포인트보다 높은 sequence number를 가진 작업들만 replica 샤드 내에서 수행되었는지 확인하면 됨

 

13.3 체크포인트의 의의

  • 결국, 체크포인트는 es가 샤드를 복구하는 과정에서 작업 수행 history를 훑을 때 발생하는 부하를 줄여주는 역할

 

14. ES에서 document 저장하는 방법

 

15. Versioning 정리

  • es는 모든 document에 대해 _version이라는 메타데이터를 저장함
    • _version의 값은 항상 정수 (Integer)
    • document를 수정할 때마다 값이 1 증가
    • document 삭제 시 _version 값은 60초까지 보존됨 (이는 설정 내 index.gc_deletes 값을 수정하면 변경 가능)
    • _version 값은 document를 검색할 때 확인 가능
  • es에서 기본적으로 제공하는 versioning을 internal versioning이라고 부름
  • external versioning도 있는데 이는 es 뿐만 아니라 다른 저장소도 사용할 때 유용한 기능

 

16. Versioning이 기존에 필요했던 이유

  • es는 git처럼 전체 history를 모두 보관하지 않고 최신 버전만 저장함
  • 즉, 정리를 하자면 versioning을 한다고 해서 인덱스 혹은 document의 과거 형상을 파악할 수 있다는 게 아님
  • 그렇다면 왜 필요할까?
    • 과거에는 _version 필드를 통해 동시성 제어를 제공했음 (현재는 더 나은 방법으로 제공)
    • 레거시 프로젝트에서 레거시 버전의 es를 사용할 경우 해당 개념을 알 필요가 있음

 

17. ES 내 동시성 제어해야 하는 이유와 방법 (Optimistic Concurrency Control)

  • es 내 저장 작업은 비동기로 진행되기 때문에 작업 순서가 꼬일 수 있는 확률이 높음
  • 동시성 제어가 필요한 케이스를 아래의 예시를 통해 설명
    • 쇼핑몰에서 고객이 선풍기를 장바구니에 추가한 뒤 결제까지 전부 진행하였고 애플리케이션은 이에 따라 선풍기를 es로부터 가져온 상태 (재고 5개라고 가정)
    • 이와 동시에 다른 고객이 선풍기를 장바구니에 추가한 뒤 결제까지 전부 진행하였고 어플리케이션은 마찬가지로 선풍기를 es로부터 가져온 상태 (아직 첫 번째 고객에 대한 처리가 완료되지 않은 상태이므로 마찬가지로 재고 5개인 상태)
    • 두 쓰레드 모두 동일한 상품을 es로부터 가져왔으므로 재고 상태는 동일하고 둘 다 결제 처리 후 재고가 4개인 상태로 저장 (원래는 첫 번째 고객 처리 후 두 번째 고객을 처리하여 재고가 3개 여야 함)

 

 

  • 앞선 예시를 방지하기 위해서는 es에서 불러온 document가 수정되었을 경우 document 업데이트가 진행되지 못하도록 방지해야 함
    • 과거에는 document의 _version 필드를 조회한 뒤 업데이트 요청에 쿼리 파라미터로 _version 값을 함께 전달하는 방법으로 해결
    • 현재는 앞서 설명한 primary terms와 sequence number를 통해 동시성 제어를 진행
    • 선풍기 상품을 es로부터 불러올 때 그 당시 primary term과 sequence number를 결과 필드를 통해 파악할 수 있음
    • 재고를 1 감소시키기 위해 업데이트 요청을 할 때 "if_seq_no"와 "if_primary_term" 파라미터에 갱신된 sequence_number와 primary term 값을 넣고 POST 요청을 보냄
    • es는 이 두 값을 전달받고 두 번째 선풍기 구매자 결제를 처리할 때 재고가 4인 상태로 업데이트가 되지 않도록 방지해줌 (optimistic concurrency control 성공!)

 

17. update by query 정리

  • RDBMS의 UPDATE WHERE 쿼리처럼 싱글 쿼리로 다수의 document를 업데이트할 때 쓰이는 쿼리
  • update by query는 앞서 설명한 개념인 primary terms, sequence numbers, 그리고 동시성 제어(Optimistic concurrency control)를 사용
  • update by query는 순서대로 document를 업데이트하다가 실패할 경우 이후 document는 업데이트 안 함
    • 예를 들자면, A B C document를 순서대로 업데이트할 때 A는 성공 B는 실패할 경우 C에 대해서는 업데이트 시도도 안 함
    • B에서 실패하더라도 C에서 업데이트 시도를 하고 싶다면 쿼리 내 "conflict": "proceed"를 추가해주면 됨
  • update by query 과정은 https://jaimemin.tistory.com/1881 참고

 

* 조회 쿼리와 bulk requests들은 replication group 내 순차적으로 전송되며 es에서 최대 10번 재시도를 진행

* 10번을 시도했음에도 불구하고 쿼리가 실패할 경우, 쿼리 요청이 중단되며 RDBMS의 Transactional과 달리 이미 변경된 document들은 롤백이 되지 않음

 

18. ES에서 Snapshot을 사용하는 방법

  • 스냅샷이 생성된 후 변경된 내용을 덮어쓰지 않도록 방지
  • 각 document의 primary term과 sequence number가 사용되는데 이는 스냅샷이 생성된 후 변경되었는지 여부를 확인하기 위한 용도
  • document는 스냅샷의 primary term과 sequence number가 동일해야지만 업데이트를 진행 (optimistic concurrency control)

 

19. delete by query 정리

  • update by query처럼 싱글 쿼리로 다수의 document를 제거할 때 쓰이는 쿼리
  • update by query와 동작 방식은 완전히 동일

 

 

 

20. Bulk API

  • 싱글 쿼리로 index, create, update 그리고 delete 쿼리를 동시에 진행할 수 있는 방법
  • Bulk API를 사용하기 위해서는 NDJSON 규격의 데이터들이 필요 (Content-Type: application/x-ndjson)
  • Bulk API 쿼리는 모든 줄이 \n 혹은 \r\n으로 끝나야 하며 마지막 줄 또한 \n 혹은 \r\n으로 끝나야 함
    • es 콘솔 창에서는 자동으로 적용되며 텍스트 에디터 같은 경우에는 별도로 \n 혹은 \r\n 추가할 필요 없이 엔터를 통해 개행을 진행해주면 됨 (단, 마지막 줄 또한 엔터를 눌러줘야 함)
  • update by query와 달리 Bulk API는 중간에 쿼리가 실패하더라도 다음 쿼리를 계속 진행
  • 따라서, Bulk API는 결괏값으로 상세한 정보를 반환하며 items 필드를 통해 해당 쿼리가 성공했는지 확인 가능하며 errors 필드를 통해 어디서 에러가 발생했는지 파악할 수 있음
  • 직접 쿼리를 작성하기보다는 자동으로 생성되는 스크립트를 활용하는 편이 효율적

 

출처

Udemy (elasticsearch-complete-guide)

반응형