저희는 어떠한 물건을 검색할때 단어의 순서를 정확하게 기억하고 검색하지는 않습니다.
예를 들면 맥북을 검색하고 싶을때 맥북 14 에어 , 맥북 에어 14 , 맥북 에어 14 M4 등 다양한 형태로 검색을 진행합니다.
이를 MySQL로 진행할 시 SELECT * FROM products WHERE name = “%맥북 14 에어%”; 라고 검색을 한다면, 정확히 일치하는 단어가 없을시 실패 할 것입니다.
하지만 Elasitcsearch의 검색은 역인덱스를 활용하여, 원하는 데이터를 추출합니다.
역인덱스
•
필드 값을 단어마다 쪼개 찾기 쉽게 미리 정리해놓은 목록
POST /products/_create/1
{
"name": "Apple 2025 맥북 에어 13 M4 10코어"
}
POST /products/_create/2
{
"name": "Apple 2024 에어팟 4세대"
}
POST /products/_create/3
{
"name": "Apple 2024 아이패드 mini A17 Pro"
}
Markdown
복사
위와 같은 3개의 도큐먼트를 Elasticsearch에 저장한다고 가정하면, 내부적으로 아래와 같이 데이터를 저장합니다.
1.
단어 단위로 자르기
"name": "Apple 2025 맥북 에어 13 M4 10코어"
→ [Apple, 2025, 맥북, 에어, 13, M4, 10코어]
"name": "Apple 2024 에어팟 4세대"
→ [Apple, 2024, 에어팟, 4세대]
"name": "Apple 2024 아이패드 mini A17 Pro"
→ [Apple, 2024, 아이패드, mini, A17, Pro]
Markdown
복사
2.
역인덱스로 저장
•
products의 name 필드에 대한 역인덱스
토큰 | 도큐먼트 |
Apple | [1, 2, 3] |
2025 | [1] |
맥북 | [1] |
에어 | [1] |
13 | [1] |
M4 | [1] |
10코어 | [1] |
2024 | [2, 3] |
에어팟 | [2] |
4세대 | [2] |
아이패드 | [3] |
mini | [3] |
A17 | [3] |
Pro | [3] |
Apple 2024 아이패드 라고 검색하면 역인덱스를 활용해 일치하는 단어가 많은 도큐먼트를 우선적으로 조회합니다. Score가 높은 순
•
id가 1인 도큐먼트 : 단어 1개 일치
•
id가 2인 도큐먼트 : 단어 2개 일치
•
id가 3인 도큐먼트 : 단어 3개 일치
score 계산 로직은 아래와 같으며, 추후에 상세하게 알아두면 좋을 것 같습니다.
1.
Term Frequency (TF)
문서 내에서 검색어가 얼마나 자주 등장하냐
→ 많이 등장할수록 점수↑
2.
Inverse Document Frequency (IDF)
검색어가 전체 문서 중 얼마나 희귀하냐
→ 희귀할수록 점수↑ (흔한 단어일수록 점수↓)
3.
Field Length Normalization
문서(필드)가 짧을수록 점수↑
→ 검색어가 짧은 문서에서 등장하면 더 관련성 높다고 판단
이러한 동작 구조는 데이터 타입이 Text일 경우에만 적용됩니다.
역인덱스 조회와 MySQL 인덱스 조회의 Trade-off 관계
1.
쓰기 증폭
B-Tree는 문서 하나를 INSERT할 때 인덱스가 걸린 컬럼 개수만큼만 갱신합니다.
인덱스 3개면 트리 3개에 각각 1건씩.
역인덱스는 문서 하나에 토큰 개수만큼 posting list를 건드립니다. 500단어짜리 문서에서 중복 제거 후 200개의 고유 term이 나오면, 200개의 posting list에 doc id를 추가해야 합니다.
MySQL: INSERT 1건 → 인덱스 3개 갱신
ES: INSERT 1건 → term 200개 × (posting list + term frequency + position) 갱신
Plain Text
복사
여기에 색인 시점 분석(형태소 분석, nori 같은 건 특히 무겁습니다) 비용까지 붙습니다. 읽기를 빠르게 하려고 쓰기 비용을 앞당겨 지불하는 구조입니다.
그래서 ES는 이걸 배치로 상쇄합니다 → 메모리 버퍼에 모았다가 1초에 한 번 세그먼트로 떨굽니다. 대신 그 1초가 near real-time이라는 또 다른 대가가 됩니다.
2.
UPDATE/DELETE 비용
필드 하나만 바꿔도 문서 전체가 다시 색인됩니다. 200개 term을 전부 다시 씁니다. MySQL이 해당 컬럼의 인덱스 페이지만 건드리는 것과 대비됩니다.
term이란?
{
"name": "카카오프렌즈 라이언 쿠션",
"description": "부드러운 극세사 소재로 제작된 대형 쿠션입니다. 거실 소파나 ... (200자)",
"stock": 100
}
이 문서 1건을 분석기에 통과시키면:
[부드럽다, 극세사, 소재, 제작, 대형, 쿠션, 거실, 소파, 침대, 사용, 커버, 분리, 세탁, 가능, ...]
으로 분리되서 저장하는 것을 말합니다.
Markdown
복사
재색인 시 실제로 일어나는 일
새 세그먼트 _1 을 만들면서
├─ "부드럽다" → posting list에 [새 doc id] 추가
├─ "극세사" → posting list에 [새 doc id] 추가
├─ "소재" → posting list에 [새 doc id] 추가
├─ ... (이 문서의 term 200개)
└─ 각 term마다 term frequency, position 정보도 함께 기록
기존 세그먼트 _0 은
└─ .del 파일에 "doc 0번 죽음" 비트만 세움 ← 여기가 전부
Markdown
복사
3.
스키마 변경이 힘듦
B-Tree 인덱스는 ALTER TABLE ... ADD INDEX로 나중에 추가·삭제가 자유롭습니다. 역인덱스는 분석기가 색인 시점에 이미 적용되어 굳어버립니다.
동의어 사전을 고치거나, tokenizer를 standard에서 nori로 바꾸거나, 필드 타입을 text에서 keyword로 바꾸는 순간 → 전체 reindex입니다. 수억 건이면 몇 시간짜리 작업이고, 무중단으로 하려면 alias 스위칭 전략이 필요합니다.
MySQL관계형 데이터 베이스에서의 한계점을 역인덱스를 통해 해결한 것을 알수있었고 이를 통해 각각의 장단점을 잘 알고 적용해야 될 것으로 생각되었습니다.

