Elasticsearch 입문 7편 — Document CRUD·Bulk·Versioning·Optimistic Locking

2026-05-19Elasticsearch 입문에서 운영까지

Elasticsearch 입문 7편. Document CRUD·Bulk·Versioning·OCC. RDBMS CRUD 와 다른 점, near-realtime 의미, Bulk size 가이드.

📚 Elasticsearch 입문에서 운영까지 · 7편 — Document CRUD·Bulk·Versioning·Optimistic Locking

이 글은 Elasticsearch 입문에서 운영까지 시리즈 38편 중 7편이에요. 5편에서 인덱스 자체의 생성·alias·reindex 를 잡았다면, 이번 7편은 그 인덱스 안에 들어가는 문서 하나하나 를 어떻게 넣고 꺼내고 바꾸고 지우느냐 — Document CRUD 깊이로 들어갑니다.

📚 학습 노트

이 글은 Elasticsearch 8.x 공식 Document APIs 문서를 한국어 학습 노트로 풀어쓴 자료예요. Bulk API · Versioning · Optimistic Concurrency Control 처럼 운영에 직결되는 자리를 RDBMS CRUD 와 비교하면서 정리했어요.

Docker 로 Elasticsearch 8.x 를 띄워 두고 curl 또는 Kibana Dev Tools 에서 한 줄씩 직접 쳐 보면 본문이 훨씬 잘 박혀요.

RDBMS CRUD 와 비교하면 어떻게 다른가

자바 백엔드 입문 시리즈 44~50편에서 잡은 JPA·SQL 의 CRUD 와 Elasticsearch CRUD 는 겉모양은 같지만 속이 많이 달라요. 같은 단어를 쓰지만 행동 모델 이 달라서, 그 차이를 먼저 잡고 들어가야 7편 나머지가 머리에 박혀요.

가장 큰 차이는 near real-time 이라는 말 한 줄에 압축돼요. SQL INSERT … COMMIT 직후 곧바로 SELECT 하면 그 행이 보여요 — 트랜잭션 일관성이 보장된다는 뜻이에요. Elasticsearch 는 다릅니다. 색인 직후 곧바로 _search 를 날려도 기본 1초 가 지나기 전에는 그 문서가 검색 결과에 안 잡혀요. 이걸 refresh interval 이라고 부르고, 이 1초 동안 새 문서들은 in-memory buffer 에 모였다가 Lucene segment 로 한 번에 flush 돼요. 3편 Lucene Internals 에서 깊이 다룬 자리.

두 번째 차이는 문서 갱신 모델 이에요. RDBMS 는 UPDATE … SET col = …자리(in-place) 에서 컬럼 값을 바꿔요. Elasticsearch 는 다릅니다. Lucene segment 가 immutable 이라 진짜 의미의 in-place update 가 X. _update API 가 내부적으로 원본 가져오기 → JSON 머지 → 새 문서로 재색인 → 옛 문서 tombstone 표시 흐름으로 동작해요. 즉 update 비용 ≒ index 비용 + get 비용. 빈번한 부분 갱신이 많은 워크로드는 ES 가 별로 안 어울려요.

세 번째 차이는 동시성 제어 입니다. RDBMS 는 row-level lock + 트랜잭션 으로 동시성을 잡아요. ES 는 락이 X. 대신 Optimistic Concurrency Control (OCC, 낙관적 동시성 제어) 을 씁니다. "내가 본 버전이 여전히 최신이면 쓰고, 아니면 충돌 던질게" 모델인데, 8편의 5장에서 깊이.

네 번째는 Bulk 단위 작업이에요. RDBMS 에서는 INSERT INTO … VALUES (…), (…), (…) 같은 multi-row insert 가 최적화 자리지만 필수는 아니에요. ES 에서는 다릅니다. 단일 색인 API 가 1초당 수백 건만 받아도 빈번한 사고. 1만 건 이상은 거의 항상 Bulk API 를 통해 수 MB 단위 NDJSON 으로 보내야 해요. 이건 권장 이 아니라 사실상 필수 입니다.

다섯 번째 차이는 문서 단위 immutable id 보장 이에요. RDBMS PK 는 AUTO_INCREMENT 가 흔하지만, ES _id클라이언트가 직접 지정 하거나 자동 생성 둘 중 하나예요. 그리고 한 번 만든 _id바꿀 수 없어요. 같은 _id 로 PUT 하면 update + version 증가 가 일어나요.

이 다섯 가지 차이를 머리에 박은 채로 7편 나머지를 읽으면 왜 이런 API 모양이 됐는지 가 자연스럽게 풀려요.

Index API — 문서 생성·갱신

Index API 는 ES 의 "문서 한 건 색인하기" 를 책임지는 API 예요. RDBMS 의 INSERT + UPDATE 가 한 자리에 묶여 있다고 보면 됩니다. 모양은 HTTP PUT 또는 HTTP POST 둘 다 됩니다.

PUT /products/_doc/1
{
  "name": "맥북 프로 14인치",
  "price": 2890000,
  "category": "노트북",
  "in_stock": true
}

요청 본문은 JSON 객체 한 개 이고, 응답은 다음과 같은 모양이에요.

{
  "_index": "products",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": { "total": 2, "successful": 2, "failed": 0 },
  "_seq_no": 0,
  "_primary_term": 1
}

여기서 resultcreated새로 만들어졌다, updated같은 _id 가 이미 있어서 덮어썼다 라는 뜻이에요. 같은 _id 로 두 번째 PUT 을 날리면 _version 이 2 로 올라가고 resultupdated 로 바뀝니다.

_doc vs _create 의 결정적 차이

URL 의 _doc 자리에 _create 를 쓰면 "이미 있는 문서면 거부" 가 됩니다. upsert 가 아닌 진짜 insert 자리.

PUT /products/_create/1
{ ... }

같은 _id 가 이미 있으면 409 Conflict 가 떨어져요. 회원가입처럼 PK 중복을 절대 허용 X 인 자리에서 씁니다. 반대로 _docPUT 이 upsert 처럼 동작 해서 같은 _id 가 있으면 덮어쓰기.

_id 를 지정하지 않으면 POST /index/_doc (PUT X) 만 가능하고, ES 가 base64 인코딩된 20자 UUID 를 자동으로 만들어 줘요. 이 자동 ID 는 시간 정렬 가능한 형태(K-sorted) 라서 append-only 워크로드에서 색인 성능에 유리해요.

refresh 옵션 — 함부로 쓰면 클러스터 죽음

Index API 에는 ?refresh 쿼리 파라미터가 있어요. 세 가지 값이 가능합니다.

refresh=false기본 이에요. 1초 뒤 다음 refresh cycle 때 검색에 노출돼요.

refresh=wait_for다음 refresh 가 일어날 때까지 응답을 미뤘다가 반환해요. 응답이 돌아왔다는 건 이미 검색에 노출됐다 라는 뜻. 테스트나 바로 검색해야 하는 사용자 경험 자리에서 유용.

refresh=true지금 즉시 refresh 를 강제 해요. 한 건마다 즉시 검색 가능. 이 옵션을 운영에서 절대 디폴트로 쓰면 X. 매 색인마다 Lucene segment 가 새로 만들어져서 클러스터가 세그먼트 폭증 + IOPS 폭증 으로 죽어요. 8장 사고 사례에 자주 등장.

Get API — 문서 한 건 꺼내기

Get API_id 로 문서 한 건 꺼내기 자리예요. RDBMS 의 SELECT … WHERE id = ? 와 같은 자리고요, PK 단건 조회만 가능. 다른 조건으로 꺼내려면 14편 Search API 로 가야 해요.

GET /products/_doc/1

응답.

{
  "_index": "products",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "name": "맥북 프로 14인치",
    "price": 2890000,
    "category": "노트북",
    "in_stock": true
  }
}

_source 자리에 원본 JSON 이 그대로 박혀 있어요. ES 는 색인 시점에 원본을 별도 _source 필드 에 통째로 저장해 두고, 검색·get 시점에 그걸 돌려줘요.

_source 필드 선택

큰 문서에서 몇몇 필드만 필요 할 때는 _source_includes / _source_excludes 로 필드 단위 필터링이 됩니다.

GET /products/_doc/1?_source_includes=name,price

응답에서 _source{ "name": "...", "price": 2890000 } 만 담겨 와요. 큰 nested 객체가 들어 있는 문서에서 네트워크 트래픽 절감 자리.

Get API 는 realtime (검색은 near-realtime)

여기서 시리즈에서 가장 헷갈리는 자리 가 등장해요. Get API 는 realtime, Search API 는 near-realtime 이에요.

방금 PUT 한 문서를 곧바로 GET /index/_doc/1 로 꺼내면 refresh 와 무관하게 바로 보여요. ES 가 in-memory translog 에서 직접 꺼내 주기 때문이에요. 같은 문서를 POST /index/_search 로 검색하면 기본 1초가 지나야 잡혀요. 이 차이는 재고 확인 → 결제 같은 워크플로에서 PK get 으로 검증할지 search 로 검증할지 결정할 때 결정적.

Update API — 부분 갱신

Update API문서 일부만 바꾸는 자리예요. 모양은 POST /index/_update/id. 세 가지 모드가 있어요.

Mode 1 — doc 머지

가장 흔한 자리. 바꿀 필드만 보내면 ES 가 원본을 가져와서 머지 후 재색인 해 줘요.

POST /products/_update/1
{
  "doc": {
    "price": 2790000,
    "in_stock": false
  }
}

namecategory 는 그대로 두고 price · in_stock 두 자리만 바뀌어요. 이 모드는 RDBMS 의 partial update 와 가장 비슷한 사용감.

Mode 2 — script

조건부 갱신·산술 연산·배열 push 같은 복잡한 로직은 Painless script 로 처리합니다.

POST /products/_update/1
{
  "script": {
    "source": "ctx._source.price -= params.discount",
    "params": { "discount": 100000 }
  }
}

원자적이지만 서버 CPU 부담 이 크고 동시 update 가 잦으면 version conflict 가 폭증해요. 운영에서는 bulk script update 보다 클라이언트에서 doc 머지로 풀기 가 권장.

Mode 3 — upsert

"있으면 update, 없으면 insert" 자리예요. RDBMS 의 MERGE / UPSERT 와 같은 모양.

POST /products/_update/1
{
  "doc": { "view_count": 1 },
  "doc_as_upsert": true
}

doc_as_upsert: true문서가 없을 때 doc 자체를 새 문서로 색인. 조회수·좋아요 누적처럼 최초 도달 시점을 모르는 자리에서 자연스러워요.

Update 의 진짜 비용

다시 한 번 강조 — ES 의 Update API 는 겉모양만 update 고 속은 reindex 예요. get + 머지 + put + tombstone 흐름이라 비용이 단순 index 보다 비싸요. 1초에 수천 건씩 update 가 들어오는 워크로드는 Kafka → 배치 처리 → bulk index 패턴으로 풀어야지, 직접 update API 를 쏘면 안 돼요.

Delete API — 문서 삭제

Delete API 는 모양이 가장 단순해요. DELETE /index/_doc/id.

DELETE /products/_doc/1

응답에서 result: "deleted" 면 성공, "not_found" 면 없어서 못 지웠다는 뜻이에요. 한 가지 함정 — 삭제 직후에도 디스크는 곧바로 안 줄어요. ES 는 tombstone 만 표시해 두고, 나중에 segment merge 가 일어날 때 실제로 사라져요. 디스크 사용량으로만 보면 "지웠는데 왜 안 줄어들지" 라는 사고가 자주 발생.

Delete by Query

조건에 맞는 다수 문서를 한 번에 지우는 API 가 따로 있어요. POST /index/_delete_by_query.

POST /products/_delete_by_query
{
  "query": {
    "term": { "category": "단종모델" }
  }
}

내부적으로 scroll + 매 배치마다 bulk delete 형태로 동작해서 대량 삭제에 안전한 편. 다만 오래 걸리는 자리 라 응답을 기다리지 말고 ?wait_for_completion=false비동기 task 로 돌리는 게 운영 권장.

대량 삭제가 잦은 워크로드는 시간 단위 인덱스 + ILM (Index Lifecycle Management) 패턴으로 풀어요. 그냥 인덱스 자체를 통째로 drop 하는 게 delete by query 보다 압도적으로 싸요. 6편 ILM 에서 깊이.

Bulk API — 운영의 진짜 자리

운영 환경에서 대량 색인·갱신·삭제 는 거의 100% Bulk API 로 들어가요. 단일 API 보다 10~100배 빠르고, 네트워크 RTT (Round-Trip Time, 왕복 시간) 비용을 한 방에 줄여요.

NDJSON 포맷

Bulk API 가 받는 본문은 일반 JSON 이 X 예요. NDJSON (Newline-Delimited JSON, 줄 단위 JSON) 입니다. 각 줄이 JSON 하나 이고, 액션 라인 + 데이터 라인 이 짝을 이뤄요.

POST /_bulk
{ "index": { "_index": "products", "_id": "1" } }
{ "name": "맥북 프로 14인치", "price": 2890000 }
{ "index": { "_index": "products", "_id": "2" } }
{ "name": "맥북 프로 16인치", "price": 3590000 }
{ "delete": { "_index": "products", "_id": "99" } }
{ "update": { "_index": "products", "_id": "3" } }
{ "doc": { "price": 2790000 } }

규칙 두 가지 — 액션 라인은 한 줄에 끝나야 함, 마지막에 개행 한 줄이 반드시 있어야 함. 줄바꿈을 잘못 넣으면 전체 요청이 400 으로 거부 됩니다. cURL 로 직접 보낼 때 가장 많이 만나는 사고.

Batch size — 5~15MB 가이드

한 번에 얼마나 넣을지가 자주 헷갈리는 자리예요. 공식 권장은 요청당 5~15MB 입니다. 문서가 작으면 1,000~5,000건, 크면 수백 건.

너무 작으면 RTT 절감 효과가 줄어들고, 너무 크면 클러스터 한 노드에서 메모리 압박 → circuit breaker 가 떨어져요. 실제 트래픽으로 1MB · 5MB · 10MB · 20MB 네 가지를 벤치 한 뒤 가장 처리량 좋은 자리 를 고르는 게 표준 접근.

Partial Failure — 응답 모양

Bulk API 의 진짜 함정은 "부분 성공·부분 실패" 자리예요. HTTP 응답 코드가 200 OK 라도 본문 내부에 일부 항목이 실패해 있을 수 있어요.

{
  "took": 30,
  "errors": true,
  "items": [
    { "index": { "_id": "1", "status": 201, "result": "created" } },
    { "index": { "_id": "2", "status": 409, "error": { "type": "version_conflict_engine_exception", ... } } }
  ]
}

응답에서 errors: true 면 그 안에 어느 한 줄이 실패 했다는 뜻. 클라이언트 코드에서 반드시 errors 플래그 + 각 item 의 status 를 확인 해야 해요. 그냥 HTTP 200 만 보고 끝 내면 데이터 손실 사고 가 조용히 누적돼요. 8장에서 다시.

클라이언트 라이브러리 헬퍼

Spring Data Elasticsearch · Python elasticsearch-py · Node @elastic/elasticsearch 같은 공식 클라이언트는 Bulk Helper 를 제공해요. streaming bulk · parallel bulk 같은 자리에서 재시도·백오프·청크 분할 까지 자동으로 잡아 줘서, 직접 NDJSON 을 조립하기보다 이걸 쓰는 게 표준.

Versioning — _version · _seq_no · _primary_term

이번 7편의 가장 깊은 자리예요. ES 는 모든 문서에 버전 메타데이터 를 박아 두고, 낙관적 동시성 제어 의 기반으로 씁니다.

_version 자동 증가

같은 _id 로 색인할 때마다 _version1씩 올라가요. 새 문서면 1, 첫 update 면 2, 두 번째 update 면 3.

이걸 이용한 가장 단순한 OCC 모양이 "내가 본 _version 이 여전히 최신이면 갱신" 인데, 8.x 부터는 권장 X 입니다. 분산 환경에서 primary 가 바뀌면 _version 만으로 동시성이 안 잡혀요. 그래서 8.x 는 두 필드를 함께 봐요 — _seq_no_primary_term.

_seq_no · _primary_term — 8.x 표준

_seq_no (Sequence Number) 는 한 primary shard 안에서 단조 증가 하는 일련번호예요. primary 가 바뀌면 다시 시작이 X, 그대로 이어져요.

_primary_termprimary 가 새로 선출될 때마다 1씩 올라가는 임기 번호 예요. primary 가 바뀌었느냐 의 신호.

두 값을 함께 비교하면 primary 가 갈리고 split-brain 같은 상황이 일어나도 동시성을 정확하게 잡을 수 있어요.

OCC 사용 모양

PUT /products/_doc/1?if_seq_no=42&if_primary_term=5
{ ... }

ES 는 현재 문서의 _seq_no 가 42 이고 _primary_term 이 5 일 때만 색인을 받아요. 다른 값이면 409 Conflict + version_conflict_engine_exception 을 던집니다.

워크플로 패턴.

  1. GET 으로 문서 + _seq_no · _primary_term 가져오기.
  2. 클라이언트에서 수정한 새 문서 만들기.
  3. PUT … ?if_seq_no=…&if_primary_term=… 로 색인.
  4. 409 면 1번부터 다시 (= retry).

재고 차감·잔액 갱신처럼 동시 update 가 흔한 자리 에서 RDBMS 의 SELECT FOR UPDATE 자리 를 대체합니다.

External Versioning — 외부 시스템과 sync

?version_type=external 옵션을 쓰면 클라이언트가 직접 버전 번호를 지정 할 수 있어요.

PUT /products/_doc/1?version=1747600000&version_type=external
{ ... }

ES 는 외부에서 받은 version 이 현재 저장된 version 보다 클 때만 색인을 받아요. RDBMS 의 updated_at timestamp 를 그대로 ES 에 sync 할 때 오래된 이벤트가 늦게 도착해 최신 데이터를 덮어쓰는 사고 를 막아 줘요. CDC (Change Data Capture, 변경 데이터 캡처) 파이프라인에서 거의 필수.

자주 만나는 사고

사고 1 — refresh=true 폭주

원인 — 개발 환경에서 "바로 검색되게 하자" 라며 refresh=true 를 운영까지 끌고 옴. 매 색인이 Lucene segment 를 즉시 만들어 segment 수 폭증 → merge thread 폭증 → IOPS 폭증 으로 클러스터 사망.

해결 — 운영은 refresh=false (기본) 만 사용. "색인 직후 바로 보여야 한다" 는 요구는 refresh=wait_for 또는 Get API (realtime) 로 풀기.

사고 2 — Bulk size 폭증

원인"한 번에 많이 넣으면 빠르다" 라며 100MB · 200MB 짜리 bulk 요청을 날림. ES 한 노드의 circuit breakerparent · request 한도를 넘어 전체 요청 reject. 최악의 경우 노드 OOM.

해결요청당 5~15MB 가이드를 지키고, 문서 수 가 X 메가바이트 로 분할 기준 잡기. 공식 클라이언트의 streaming bulk helper 가 자동 분할을 해 줘요.

사고 3 — Bulk partial failure 무시

원인 — Bulk API 가 HTTP 200 으로 응답해서 전체 성공인 줄 알았는데 사실은 일부 항목이 version_conflict · mapper_parsing · circuit_breaking 으로 떨어져 있었음. 클라이언트가 errors 플래그를 안 봐서 데이터 손실 누적.

해결반드시 응답의 errors 플래그 검사 + 각 item 의 status 가 2xx 인지 확인. 실패 항목은 DLQ (Dead Letter Queue, 처리 실패 메시지 보관소) 로 격리하고 retry 정책 운용.

사고 4 — script update 동시성 충돌

원인 — 좋아요 카운트를 ctx._source.likes += 1 script 로 매 클릭마다 업데이트. 같은 문서에 동시 요청이 몰리면 version_conflict 폭증.

해결Kafka 로 이벤트 누적 → 1초 단위 집계 → bulk update 패턴. 또는 retry_on_conflict 파라미터를 5~10 으로 설정. 운영 트래픽이 큰 자리는 카운터를 ES 가 아닌 Redis 로 빼는 게 정석.

사고 5 — Delete by Query 의 락 환상

원인"Delete by Query 가 트랜잭션처럼 락 잡고 도는 줄 알았는데, 실제로는 scroll 기반이라 삭제 진행 중에 새로 들어온 문서는 안 지워질 수도, 옛 버전이 삭제될 수도 있음.

해결대량 삭제는 인덱스 통째로 drop (ILM rollover) 으로 풀고, 정말 필요하면 refresh=true 후 delete by query 패턴. 가능하면 애플리케이션에서 is_deleted flag 만 박고 ILM 으로 정리.

사고 6 — Version Conflict 폭주

원인 — 같은 문서에 초당 수십 건 update 가 들어오는데 OCC 를 켜 둠. 409 가 폭주 하면서 클라이언트 retry 폭증 → 클러스터 부하 가중.

해결update 경합이 큰 자리는 Redis 같은 단일 스토어로 빼기. ES 에는 집계된 결과만 주기적으로 반영. 또는 retry_on_conflict 로 ES 가 자체적으로 N회 재시도하게 풀기.

사고 7 — _id 자동 생성 vs 지정 혼용

원인 — 같은 인덱스에 _id 자동 생성 색인_id 지정 색인 이 섞여 들어옴. 재실행 시 같은 데이터가 새 _id 로 또 색인 되어 중복 문서 폭증.

해결동일 식별성을 가지는 문서는 반드시 _id 지정 (= 자연 키 또는 해시). 자동 ID 는 append-only 로그 같은 자리 에서만. 재실행 멱등성 이 필요한 자리는 100% _id 지정.

운영 권장 패턴

운영에서 Document CRUD 를 안정적으로 굴리려면 네 가지 패턴이 거의 항상 박혀 있어요.

첫째, bulk 가 기본이고 단일 API 는 예외 로 운용해요. 단건 색인이 분당 100건을 넘어가면 무조건 bulk 큐로 빼는 게 표준. 비동기 큐 (Kafka·SQS) 에 누적시키고, 1~5초 단위로 flush 하는 패턴이 가장 많이 쓰여요.

둘째, refresh 전략을 데이터별로 분리 합니다. 검색 인덱스는 refresh_interval: 1s (기본), 로그 인덱스는 30s, 대량 색인 시점에는 -1 (refresh 끄기) 로 잠시 박아 두고 색인 끝난 뒤 다시 켜는 bulk-friendly 운용. ILM 정책에 박아 자동화.

셋째, OCC 가 필요한 자리만 OCC 를 켜요. 모든 update 에 OCC 를 박으면 retry 폭증 으로 클러스터가 휘청해요. 동시 갱신 충돌이 실제로 잦은 문서 (재고·잔액·카운터) 에만 OCC, 나머지는 마지막 쓰기가 이김(last-write-wins) 으로 두는 게 운영 부하가 가벼워요.

넷째, 클라이언트 라이브러리의 Bulk Helper 표준화. 직접 NDJSON 조립 X. 공식 클라이언트의 streaming/parallel bulk 헬퍼를 표준 wrapper 로 감싸고, retry · backoff · DLQ 적재 까지 그 wrapper 안에서 일관 처리. 신규 프로젝트 시작 시점에 박아 두면 1년 뒤 사고가 절반으로 줄어요.

시험 직전 한 번 더 — 압축 노트

  • ES CRUD ≠ RDBMS CRUD — near-realtime, segment immutable, OCC, bulk 필수, _id 불변 다섯 가지 차이.
  • Index APIPUT /index/_doc/id 면 upsert, PUT /index/_create/id이미 있으면 409.
  • 자동 ID = POST /index/_doc 로만, base64 K-sorted UUID 자동 생성.
  • refresh = false (기본 1s) · wait_for · true. 운영에서 refresh=true 절대 X.
  • Get APIrealtime (translog 에서 직접), Search APInear-realtime (refresh 후).
  • _source_includes/_excludes 로 필요한 필드만 가져오기 가능.
  • Update API = get + 머지 + put + tombstone. 진짜 in-place X.
  • Update 3 모드 = doc 머지 · script · doc_as_upsert.
  • Delete API = tombstone 만 표시, 실제 디스크는 segment merge 때 회수.
  • Delete by Query = scroll + bulk delete. 대량 삭제는 인덱스 drop 이 정석.
  • Bulk API = NDJSON, 액션 라인 + 데이터 라인 짝, 끝에 개행 필수.
  • Bulk batch size = 5~15MB 가이드. circuit breaker 안 터지는 자리에서 max.
  • Bulk partial failureerrors 플래그 + 각 item status 반드시 검사.
  • _version = 같은 _id 마다 1씩 증가, 8.x 단독 OCC X.
  • _seq_no + _primary_term = 8.x 표준 OCC. if_seq_no · if_primary_term.
  • External Versioning = ?version=N&version_type=external, CDC 파이프라인 필수.
  • 7대 사고 = refresh=true · bulk size · partial failure · script 충돌 · delete by query 환상 · version conflict · _id 혼용.
  • 운영 4대 패턴 = bulk 기본 · refresh 분리 · OCC 선택적 · Bulk Helper 표준화.

시리즈 다른 편

  • 이전 글 = 6편 ILM — Index Lifecycle Management (Hot·Warm·Cold·Delete)
  • 다음 글 = 8편 Mapping Deep — Static·Dynamic·Multi-field·Runtime Field
  • 9편 = Field Types — text·keyword·numeric·date·boolean·object·nested
  • 10편 = Analyzer — Char Filter·Tokenizer·Token Filter
  • 11편 = 한국어 Analyzer — Nori·mecab-ko·사용자 사전
  • 14편 = Search API — Query DSL 기초
  • 23편 = Bulk·Ingest — 색인 파이프라인 깊이
  • 32편 = Spring Data Elasticsearch — Repository·Template·POJO

한 줄 정리 — ES Document CRUD = RDBMS CRUD 와 단어는 같지만 near-realtime · segment immutable · bulk 필수 · OCC 기반 동시성 네 자리가 다른 모델. Bulk API + _seq_no/_primary_term 기반 Optimistic Concurrency Control 이 운영의 핵심 두 자리.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!