Elasticsearch
Elasticsearch는 Apache Lucene 기반의 오픈소스 검색 및 분석 엔진입니다. 방대한 양의 데이터를 신속하게, 거의 실시간으로 저장, 검색, 분석할 수 있습니다. 기존의 관계형 데이터베이스가 다루기 어려웠던 비정형 데이터 검색, 특히 Full-text 검색에 매우 뛰어난 성능을 보입니다.
Elasticsearch는 단순한 검색 엔진을 넘어, 로그 분석, 실시간 데이터 시각화, 비즈니스 인텔리전스 등 다양한 분야에서 활용됩니다. 특히 ELK 스택 (Elasticsearch, Logstash, Kibana)의 핵심 구성 요소로서 데이터 수집부터 분석, 시각화까지 아우르는 데이터 파이프라인을 구축하는 데 중심적인 역할을 합니다.
주요 특징1: 분산 시스템

Elasticsearch는 Scale-out에 특화된 아키텍처를 가지고 있습니다.
- 데이터를 ‘샤드 (Shard)’ 단위로 분할합니다.
- Index를 생성할 때부터 ‘샤드’라는 단위의 여러 조각으로 나눠서 생성합니다.
- 예를 들어, 1억 개의 데이터가 담긴 Index를 생성할 때, 샤드 개수를 5개로 설정하면 2천만 개씩의 데이터가 담긴 샤드 5개가 생성됩니다.
- 새로운 서버(노드)를 추가하고 샤드를 분산시킵니다.
- 초기: 서버가 1대일 때에는 5개의 샤드가 모두 그 서버 안에 있습니다.
- 서버 추가: 데이터가 늘어남에 따라 서버 1대의 용량이 부족해지면 새로운 서버(노드)를 클러스터에 추가합니다.
- 자동 분산: 새로 추가된 노드를 감지하고, 기존 서버에 몰려있던 샤드들을 자동으로 새로운 노드로 옮겨서 분산시킵니다.
예를 들어, 1번 서버에 샤드 3개, 2번 서버에 샤드 2개를 배치하는 방식입니다.
- 검색과 색인 작업을 병렬로 처리합니다.
- 사용자가 ‘노트북’을 검색하면, Elasticsearch는 이 요청을 모든 샤드에 동시에 보냅니다.
- 각 노드는 자신이 담당하는 샤드 내에서만 ‘노트북’을 검색하고, 결과를 반환합니다.
- 마지막으로 각 샤드에서 온 결과를 취합하여 사용자에게 최종 결과를 보여줍니다.
- 이렇게 여러 노드가 동시에 일을 나누어 처리하기 때문에 데이터가 아무리 많아져도 빠른 검색 속도를 유지할 수 있습니다.
이 덕분에 Elasticsearch는 데이터가 수십억 건으로 늘어나도, 검색 요청이 폭주해도, 단순히 서버를 추가하는 것만으로 유연하고 안정적으로 시스템을 확장해나갈 수 있습니다. 이는 단일 서버의 성능에 의존하는 RDBMS와 비교되는 강력한 장점입니다.
주요 특징2: 실시간에 가까운 성능 (Near Real-Time)
Elasticsearch는 데이터를 저장하자마자 거의 즉시 검색에 활용할 수 있습니다.
- In-memory Buffer: 새로운 데이터 (Document) 가 들어오면 Elasticsearch는 디스크에 바로 쓰지 않고 일단 메모리 버퍼에 저장합니다.
- Refresh: 기본적으로 1초마다
refresh
과정이 일어납니다. 이때 메모리 버퍼에 있던 데이터들이 검색 가능한 상태인 ‘세그먼트 (Segment)’ 라는 작은 파일로 만들어져 캐시에 추가됩니다. - 검색 가능: 이
refresh
과정이 끝난 직후부터 데이터는 디스크에 완전히 저장되지 않았더라도 검색 결과에 노출됩니다. - Commit / Flush: 나중에 더 큰 단위로 세그먼트들이 병합되어 디스크에 영구적으로 안전하게 저장됩니다.
이는 로그 분석, SNS, E-Commerce 등 실시간으로 데이터가 반영되어야 할 때 중요한 특징입니다.
주요 특징3: RESTful API
Elasticsearch는 HTTP 를 사용해 모든 기능을 제어하고 데이터를 주고받습니다. 개발자는 Elasticsearch의 내부 동작을 알 필요 없이, REST API를 사용하여 요청 사항을 Elasticsearch에 반영합니다.
- HTTP Method
- GET: 데이터 조회/검색
- POST/PUT: 데이터 생성/수정
- DELETE: 데이터 삭제
- URL:
http://{서버주소}:9200/{index}/_search
와 같이 정해진 주소 형식과 쿼리를 통해 원하는 데이터 또는 기능에 접근합니다. - JSON: 모든 요청과 응답은 JSON 형태로 이뤄집니다.
이를 통해 Python, Java, Go 등 HTTP 요청을 보낼 수 있는 어떤 프로그래밍 언어로도 Elasticsearch를 제어할 수 있습니다. 또는 curl 같은 간단한 명령줄 도구로도 서버를 직접 테스트하고 관리할 수 있습니다.
Elasticsearch 핵심 아키텍처

- 클러스터
- 하나 이상의 노드로 구성된 Elasticsearch 시스템의 가장 큰 단위입니다.
- 전체 데이터와 검색 기능을 제공하며, 모든 노드를 사용합니다.
- 노드
- Elasticsearch를 구성하는 단일 서버 프로세스입니다.
- 클러스터에 속한 구조이며, 실제 데이터 저장 및 검색 작업을 수행합니다.
- 노드는 설정에 따라 다양한 역할을 수행할 수 있으며, 클러스터의 규모와 목적에 따라 역할을 분리하여 운영하는 것이 안정성과 성능 최적화의 핵심입니다.
- 마스터 노드 (Master-eligible Node): 클러스터의 지휘관 역할입니다. 클러스터 전체의 상태 (메타데이터)를 관리하고, 인덱스 생성/삭제, 노드 추가/제거와 같은 관리 작업을 조율합니다.
- 데이터 노드 (Data Node): 데이터의 저장소이자 작업자 역할입니다. 실제 샤드를 저장하고, 데이터의 색인과 검색 작업을 수행합니다. CPU, 메모리, 디스크 I/O 등 가장 많은 리소스를 소모합니다.
- 인제스트 노드 (Ingest Node): 데이터가 색인되기 전, 문서를 사전 처리하는 가공 공장 역할입니다. 필드 추가, 값 변경, 특정 패턴 제거 등 다양한 전처리 파이프라인을 적용할 수 있습니다.
- 코디네이팅 노드 (Coordinating-only Node): 클라이언트의 요청을 받는 안내 데스크 역할입니다. 사용자의 검색 요청을 받아 적절한 데이터 노드들로 분산시키고, 각 노드에서 받은 결과를 취합하여 최종 결과를 클라이언트에게 반환합니다. 대규모 클러스터에서 검색 부하 분산을 위해 사용됩니다.
- 프로덕션 환경에서는 각 노드의 역할을 명확히 분리하여 각각 전용 노드에 할당하는 것이 장애 격리 및 성능 최적화에 매우 중요합니다.
- 샤드
- 인덱스를 여러 부분으로 나눈 조각입니다.
- 데이터의 크기가 커서 하나의 노드에 모두 저장하기 어려울 때, 샤드를 통해 데이터를 분산 저장할 수 있습니다.
- 이는 Scale-out을 가능하게 하여 시스템의 전체 저장 용량과 처리량을 늘리는 핵심 역할을 합니다.
- 레플리카
- 샤드의 실시간 복제본입니다. 단순히 데이터를 백업하는 것을 넘어, 시스템의 안정성과 검색 성능을 높이는 두 가지 중요한 역할을 수행합니다.
- 고가용성 확보: 특정 노드에 장애가 발생하여 해당 노드에 있던 원본 샤드가 유실되더라도, 다른 노드에 복제해 둔 레플리카 샤드가 즉시 원본의 역할을 대신합니다. 이 덕분에 일부 서버가 다운되어도 데이터의 유실 없이 서비스가 유지됩니다.
- 검색 성능 향상: 검색 요청이 들어왔을 때 Elasticsearch는 원본 샤드와 레플리카 샤드에 요청을 분산하여 병렬로 처리할 수 있습니다. 읽기 작업을 분산시켜 더 많은 검색 요청을 동시에 처리할 수 있게 되기 때문에, 전체 시스템의 처리량이 증가합니다.
- 샤드의 실시간 복제본입니다. 단순히 데이터를 백업하는 것을 넘어, 시스템의 안정성과 검색 성능을 높이는 두 가지 중요한 역할을 수행합니다.
RDBMS와의 비교
RDBMS | Elasticsearch | Description |
Database | Index | 문서 (Document) 들의 모임입니다. |
Table | Type | Index에서 문서를 논리적으로 분류하는 단위였으나, 현재는 (8.0 이상) 하나의 Index에 하나의 Type을 사용하는 것을 권장합니다. |
Row | Document | Elasticsearch에서 데이터가 저장하는 최소 단위이며, JSON 형식의 객체입니다. |
Column | Field | Document를 구성하는 개별 속성입니다. |
Schema | Mapping | 문서와 필드가 어떻게 저장되고 색인할지 정의하는 프로세스입니다. |
역색인 (Inverted Index)
Elasticsearch의 검색 속도가 빠른 이유는 역색인 구조 덕분입니다. 일반적인 데이터베이스가 문서 ID를 기준으로 어떤 단어들이 포함되어 있는지 기록하는 반면, 역색인은 각 단어가 어떤 문서에 존재하는지를 기록합니다.
예를 들어, 다음과 같은 두 개의 문서가 있다고 가정해봅시다.
- 문서 1: “I like search engine.”
- 문서 2: “I search keywords by google.”
역색인은 다음과 같이 구성됩니다.
단어 | 문서 |
I | 문서 1, 문서 2 |
like | 문서 1 |
search | 문서 1, 문서 2 |
engine | 문서 1 |
keywords | 문서 2 |
by | 문서 2 |
문서 2 |
“google” 이라는 단어를 검색하면, 역색인을 통해 바로 해당 단어가 “문서 2″에 속해있다는 것을 알아낼 수 있습니다.
분석기 (Analyzer)
그렇다면 Elasticsearch는 “I like search engine.”이라는 문장을 어떻게 I, like, search, engine 이라는 단어들로 쪼개어 역색인에 저장할 수 있을까요? 이 과정을 담당하는 것이 바로 분석기(Analyzer) 입니다.
분석기는 텍스트 분석을 위한 하나의 파이프라인이며, 크게 세 가지 요소로 구성됩니다.
- 캐릭터 필터 (Character Filters): 텍스트가 토크나이저로 전달되기 전, HTML 태그를 제거하거나 &를 and로 바꾸는 등 문자 단위를 전처리합니다.
- 토크나이저 (Tokenizer): 전처리된 텍스트 스트림을 실질적인 단어(Term) 단위로 분리합니다. 공백이나 특정 문자를 기준으로 쪼개는 역할을 합니다. (ex: whitespace 토크나이저)
- 토큰 필터 (Token Filters): 토크나이저가 분리한 단어들을 추가적으로 가공합니다. 대문자를 소문자로 바꾸거나(lowercase), 불필요한 단어(불용어, a, an, the 등)를 제거하거나(stop), 단어의 원형을 찾아주는(stemming) 등의 역할을 수행합니다.
예를 들어, “Quick Brown Fox!” 라는 텍스트가 lowercase 토큰 필터를 포함한 분석기를 거치면 quick, brown, fox 라는 최종 토큰으로 변환되어 역색인에 저장됩니다. 이 덕분에 사용자가 소문자인 fox로 검색해도 원본 문서의 Fox를 찾아낼 수 있는 것입니다.
특히 한글과 같이 조사가 붙고 형태소 분석이 중요한 언어는 기본 분석기로는 한계가 있습니다. nori
와 같은 한국어 형태소 분석 플러그인을 사용하여 정확한 토큰화를 수행하는 것이 검색 품질을 결정하는 핵심 요소입니다.
Elasticsearch 데이터 모델링
Elasticsearch의 강력한 성능을 제대로 활용하기 위해서는 데이터를 저장하는 방식, 즉 데이터 모델링부터 RDBMS와는 다른 접근이 필요합니다. RDBMS 설계에 익숙한 엔지니어들이 가장 흔하게 하는 실수는 바로 정규화(Normalization)된 구조를 그대로 Elasticsearch에 적용하려는 것입니다.
‘스키마리스’의 함정과 명시적 매핑의 중요성
Elasticsearch는 스키마를 미리 정의하지 않아도 데이터 타입 등을 자동으로 추론하는 동적 매핑(Dynamic Mapping)을 지원합니다. 이는 개발 초기에는 편리하지만, 프로덕션 환경에서는 의도치 않은 타입 지정(text vs keyword), 불필요한 필드 색인으로 인한 성능 저하 및 저장 공간 낭비를 유발할 수 있습니다.
따라서 데이터 엔지니어링 관점에서는 명시적 매핑(Explicit Mapping)을 통해 각 필드의 타입(keyword, text, integer, date 등)과 분석 방식을 사전에 명확히 정의하는 것이 매우 중요합니다.
Join 대신 선택한 것: 비정규화(Denormalization)
RDBMS에서는 중복을 최소화하기 위해 데이터를 여러 테이블로 나누어 정규화하고, 필요할 때 JOIN을 사용해 데이터를 조합합니다. 하지만 분산 시스템인 Elasticsearch에서 검색 시점의 JOIN은 엄청난 비용을 유발하며, 성능 저하의 주범이 됩니다.
Elasticsearch는 이를 해결하기 위해 비정규화(Denormalization)를 적극적으로 권장합니다. 즉, 검색에 필요한 데이터들을 미리 조합하여 하나의 문서(Document)에 모두 포함시키는 방식입니다.
예를 들어, 쇼핑몰의 ‘상품’ 데이터를 모델링한다고 가정해봅시다.
- RDBMS 방식: products, categories, brands 세 개의 테이블로 분리
- Elasticsearch 방식: ‘상품’ 문서 하나에 카테고리 이름과 브랜드 정보를 모두 포함
이처럼 비정규화를 통해 비효율적인 JOIN 연산을 완전히 제거하고, 단일 문서 내에서 모든 검색과 필터링이 가능하도록 만들어 검색 성능을 확보하는 것이 Elasticsearch 데이터 모델링의 핵심입니다.
데이터 수집(Ingestion) 파이프라인 아키텍처
데이터를 어떻게 Elasticsearch로 안정적이고 효율적으로 보낼 것인지 결정하는 것은 데이터 파이프라인 설계의 중요한 과제입니다. 수집하려는 데이터의 종류와 특성에 따라 다양한 아키텍처를 구성할 수 있습니다.
Beats Family: 경량 데이터 수집기(Shipper)인 Beats를 사용하는 가장 일반적인 패턴입니다. 서버 로그는 Filebeat, 시스템 메트릭은 Metricbeat, 네트워크 데이터는 Packetbeat 등을 사용하여 각 서버에서 발생하는 데이터를 직접 수집하고 Elasticsearch로 전송합니다.
Logstash (ETL 파이프라인): Beats만으로 처리하기 어려운 복잡한 데이터 가공이 필요할 때 Logstash가 중간 다리 역할을 합니다. 다양한 소스(JDBC, S3, Kafka 등)로부터 데이터를 입력받아 파싱, 필터링, 변환을 거친 뒤 Elasticsearch로 전송하는 ETL 도구입니다.
Kafka & Kafka Connect: 대용량의 스트리밍 데이터를 안정적으로 처리해야 할 때 주로 사용되는 아키텍처입니다. 모든 데이터를 먼저 메시지 큐인 Apache Kafka에 모아 버퍼링 역할을 하도록 하고, Kafka Connect (Elasticsearch Sink Connector)를 사용하여 Kafka 토픽의 데이터를 Elasticsearch로 안정적으로 전달합니다. 이 구조는 데이터 유실을 방지하고 시스템 간의 결합도를 낮추는 효과가 있습니다.
애플리케이션 직접 색인: 서비스 애플리케이션에서 발생하는 이벤트나 데이터베이스 변경 사항을 직접 Elasticsearch 공식 클라이언트(Java, Python 등)를 사용하여 색인하는 방식입니다. 데이터의 정합성이 매우 중요할 때 주로 사용됩니다.
이러한 파이프라인 전략을 이해하고 데이터의 특성에 맞는 최적의 아키텍처를 설계하는 것이 성공적인 Elasticsearch 운영의 기반이 됩니다.
결론
Elasticsearch는 이제 검색을 넘어 데이터 분석과 Observability 영역의 핵심으로 자리 잡은 분산 시스템입니다. 실시간에 가까운 데이터 처리 능력, RESTful API를 통한 유연한 제어, 그리고 샤드와 레플리카를 기반으로 한 안정적인 수평 확장은 대규모 데이터를 다루는 모든 서비스의 기술적 기반이 될 수 있습니다. 단순히 빠른 검색 기능을 구현하는 것을 넘어, 로그, 메트릭, 트랜잭션 데이터를 통합하여 시스템의 상태를 다각도로 분석하고 비즈니스 인사이트를 도출하는 데 Elasticsearch를 활용해 보시길 바랍니다. 이는 서비스의 안정성을 높이고 데이터 기반 의사결정을 가속화하는 중요한 첫걸음이 될 것입니다.
이어지는 글에서는, 분석용 데이터 웨어하우스인 BigQuery의 데이터를 웹서비스로 가져오는 과정에서 발생하는 성능 이슈를 어떻게 해결했는지 다루겠습니다. Elasticsearch를 Serving Layer로 활용하고 Spring Boot로 API를 구현하여, 수십 초에 달하던 데이터 조회 시간을 밀리초(ms) 단위로 단축시킨 사례를 소개해 드릴 예정입니다.