본문 바로가기

소프트웨어-이야기/데이터 저장소 + 시각화

elasticsearch와 RDB 데이터 저장하기

 

#시작 

오늘은 엘라스틱서치 기술 블로그에서 흥미롭게 읽었던 자료를 함께 살펴보는 시간을 가져보려고 합니다.

 

설명드릴 블로그 포스팅은 Keeping Elasticsearch in Sync입니다. 

https://www.elastic.co/kr/blog/found-keeping-elasticsearch-in-sync#the-bulk-api-a-must-for-most-applications

 

Keeping Elasticsearch in Sync

One of the trickiest parts of integrating Elasticsearch into an existing app is figuring out how to manage the flow of data from an authoritative data source, such as an SQL database, into Elasticsearch. In most cases this means utilizing Elasticsearch's b

www.elastic.co

 

#1 Elasticsearch 활용

저희 서비스에서는 엘라스틱 서치를 크게 두가지 목적으로 사용하고 있습니다. 

첫 번째는 서비스 모니터링을 위하여, 시스템 로그에 대한 접근성을 높이는 데에 활용합니다. 

 

두번째는 고객들이 상품과 브랜드를 쉽게 검색할 수 있도록 돕는 데에 활용합니다. 

오늘은 후자의 관점에서 엘라스틱서치에 데이터를 동기화하는 방법에 대해서 설명하고자 합니다. 

 

#2 데이터 동기화와 트레이드 오프

서비스에서 전문검색을 하기 위하여, 엘라스틱서치를 활용하는 경우, RDB 혹은 NoSQL 같은 신뢰할 수 있는 데이터베이스에 원본 데이터를 저장해둡니다. 엘라스틱 서치는 검색을 목적으로 하는 제 2의 저장소로 활용됩니다. 

이처럼 두 데이터가 다른 저장소에 저장되게 되면, 우리는 두 저장소의 데이터 신뢰성과 동기화 지연 시간을 고려하여 동기화 전략을 세워야합니다. 

동기화 전략은 데이터 신뢰성과 동기화 지연 시간에서 약간의 트레이드오프가 있습니다.

 

데이터 신뢰성은 낮지만, 빠르게 동기화가 된다거나

데이터 신뢰성은 높지만, 동기화되는 데에 일정 텀이 있다거나 말이죠.

 

두 저장소의 완벽한 동기화는 사실상 불가능합니다. 그리고 검색 서비스 목적상 짧으면 몇초, 길면 며칠간의 지연은 허용될 수 있는 범위입니다. 때문에 이러한 특성을 고려하여 동기화 전략을 세우는 것이 좋습니다. 

 

#3 동기화 전략 

데이터 동기화 전략은 크게 3가지 입니다.

1. 실시간 처리

2. 이벤트 큐 처리

3. 배치 처리

이어서 페이지 조회수를 집계하는 사례를 통해 각 전략의 구현 방식 및 장단점에 대해서 살펴보겠습니다. 

 

#4 실시간 처리 

실시간 처리 방식은 이벤트가 발생하는 즉시 동기화를 시키는 전략을 말합니다.

데이터베이스에 데이터를 갱신함과 동시에 엘라스틱 서치의 데이터를 갱신하는 거죠.

이 방식은 데이터가 빠르게 동기화된다는 장점이 있습니다. 그리고 별도의 프로세스를 관리하지 않기 때문에 기능 구현 또한 간단합니다.

그러나 해당 방식은 여러가지 단점이 있습니다.

 

1. 데이터 신뢰성이 떨어집니다.

위의 프로세스는 엘라스틱 서치에 문서 쓰기 요청이 유실된 경우, 해당 데이터를 복구할 수 없습니다. HTTP 요청 처리 과정 중, 오류가 발생하면 데이터의 일관성이 계속 어긋나게 됩니다.

 

2. 컴퓨팅 자원을 불필요하게 많이 사용합니다. 

위의 방식은 페이지 조회수 집계 요청이 엘라스틱 서치 HTTP 요청으로 직결되는 구조입니다.

즉, 1000번의 조회가 발생하면 엘라스틱서치에 1000번의 쓰기 요청을 보내는 구조입니다.

 

엘라스틱 서치는 읽기 비용이 낮은 반면, 쓰기 비용이 높은 편입니다. 그리고 document의 일부를 변경하는 작업은 엘라스틱서치 관점에서는 많은 비용을 필요로 합니다. 엘라스틱서치는 한번 생성되고 나서는 변경되지 않는 immutable한 특성이 있습니다. 

페이지 조회수를 1을 증분하는 작업은, 엘라스틱 서치 관점에서는 기존 데이터를 삭제했다고 내부적으로 마킹처리를 하고, 새로운 문서를 만들고, 새로 인덱싱을 하는 작업을 필요로 합니다. 

때문에 위와 같은 구조는 엘라스틱서치에 쓰기 부담을 주는 구조입니다.

document 갱신이 지연되면서, 409 error로 괴로워하게 될거에요. 

 

3. 단일 장애 지점 (single point of failure)을 발생시킬 가능성이 있습니다. 

애플리케이션의 트래픽 급증으로 인한 장애가 엘라스틱서치로 곧장 이어진다는 리스크가 있습니다. 

 

#5 이벤트 큐 처리 

이벤트 큐 처리는 큐에 이벤트 정보들을 담아두고, 워커가 주기적으로 큐를 비워주는 방식을 의미합니다.

워커는 업데이트해야하는 도큐먼트별로 이벤트 데이터를 집계한 후, 엘라스틱 서치에 한번에 싱크해주는 역할을 수행합니다.

하나의 페이지에 대한 조회수가 1000번 발생했으면, 하나의 페이지의 조회수를 1000 증분해주는 방식으로 말이죠.

그리고 동기화에 성공한 경우에만 큐를 비워줍니다. 

 

이 방식에 Elasticsearch의 Bulk API를 활용하면 효율을 더욱더 높일 수 있습니다.

Elasticsearch는 여러개의 문서를 한번에 수정하는 인터페이스를 제공하고 있습니다. 100개의 페이지의 조회수를 변경해야한다면, BulkAPI의 파라미터에 한번에 담아서 처리할 수 있는거죠. 

이 방식을 사용하면 워커가 실행되는 텀동안 약간의 동기화 지연이 발생할 수 있습니다. 

그러나 여러 장점이 있습니다. 

1. 불필요한 엘라스틱서치 쓰기 비용을 줄일 수 있습니다.

2. 애플리케이션의 비즈니스 로직과 엘라스틱서치 동기화 로직의 결합도를 줄일 수 있습니다. 

3. 단일 장애 포인트를 줄일 수 있습니다. 엘라스틱 서치에 쓰기 처리를 하는 빈도를 조절할 수 있기 때문에, 문제 상황 시 해당 워커를 꺼둘 수도 있죠. 

 

#6 배치 처리 

배치처리는 레코드가 추가된 시간과 갱신된 시간을 기반으로 데이터를 동기화하는 방식을 말합니다. 

배치 방식은 젠킨스를 통해 배치잡을 관리하는 방식도 있고, 로그스태시의 JDBC 플러그인을 사용하는 방식도 있습니다. 

 

정보가 변경되지 않고 누적되는 데이터인 경우, 레코드 생성 시각을 기준으로 데이터를 엘라스틱서치로 퍼올리면 됩니다.

레코드가 변경되는 데이터인 경우, 레코드가 업데이트된 시각을 기준으로 데이터를 엘라스틱서치로 퍼올리면 됩니다.

만약 레코드가 삭제될 수 있는 경우, is_deleted 컬럼을 추가하여 삭제여부를 레코드에 마킹처리를 한 후, 배치잡에서 싱크가 완료되면 삭제처리를 해주는 프로세싱이 필요합니다. 혹은 삭제된 당시에 실시간으로 삭제처리를 해야합니다. 

 

위와 같이 배치 처리를 하는 경우, 레코드 생성 시각과 업데이트 시각 컬럼이 실제 생성/업데이트 시각과 어긋나지 않도록 잘 관리되어야 합니다.

 

#7 마무리

문제를 해결하기 위해서는 여러가지 전략을 섞어서 사용해야하는 경우가 있습니다.

전체 재인덱싱이 필요한 경우, 배치 전략과 큐 전략을 적절하게 섞어서 사용해야할 수도 있죠. 

혹은 수정 / 삭제는 배치 방식을 사용하지만, 삭제는 실시간 동기화 전략을 사용할 수도 있습니다. 

상황에 맞게 적절하게 응용하는 것이 필요합니다.

 

그리고 전략을 적절하게 잘 활용하려면, 수정/삭제 시각 컬럼이 잘 관리되어야합니다.

 

<끝!>