바로 이전 포스팅에 이어 세그먼트 불변성에 대한 포스팅을 이어나가겠습니다.

 

세그먼트 불변성

세그먼트가 수정 불가능한 불변성을 가짐으로써 제공되는 장점들이 있다.

 

1)동시성 문제 회피

불변성이 보장된다면 Lock이 필요 없어진다. 다수의 스레드가 동작하는 복잡한 다중 스레드 환경에서 동시성 문제는 매우 중대한 문제이다. 루씬은 세그먼트의 불변성으로 이러한 동시성 문제를 간단히 피해갔다.

2)시스템 캐시 활용

데이터가 OS 커널에서 제공하는 시스템 캐시에 한번 생성되면 일정 시간 동안은 그대로 유지된다. 불변성을 보장하지 않을 경우 수정이 있을 때마다 시스템 캐시를 삭제하고 다시 생성해야하는 비용이 큰 작업을 수행하게 된다. 하지만 불변성이라면 이러한 시스템 캐시를 효율적으로 이용할 수 있다.

3)높은 캐시 적중률

불변성이기 때문에 시스템 캐시의 수명이 길어지므로 캐시의 적중률 또한 높아진다.

4)리소스 절감

역색인을 만드는 과정에서 많은 시스템 리소스(CPU,Memory)가 사용된다. 수정을 허용하게 되면 일부분이 변경되더라도 해당 역색인을 대상으로 작업해야하기 때문에 시스템 리소스가 소모된다.

 

하지만 이러한 불변성에도 단점은 존재한다. 우선 일부 데이터가 변경되더라도 전체 역색인 구조가 다시 만들어져야 하고, 또 다른 문제는 실시간 반영이 상대적으로 어려워지는 것이다. 하지만 이러한 단점을 극복하기 위해 루씬은 하나의 세그먼트를 사용하는 것이 아니라 다수의 세그먼트를 생성하기 때문에 변경 될때 마다 모든 세그먼트를 다시 만드는 것이 아니라 기존 세그먼트는 그대로 두고 추가로 세그먼트를 생성하기에 세그먼트 생성 중에 기존 세그먼트를 이용하여 검색 결과를 제공한다.

 

그렇다면 이런 불변성을 가진 세그먼트에서 수정/삭제연산은 어떻게 처리될까?

 

수정연산 같은 경우 세그먼트의 불변성을 유지하기 위해 해당 데이터를 삭제한 후 다시 추가하는 방식으로 동작한다. 기존 데이터는 삭제 처리되어 검색 대상에서 제외되고 변경된 데이터는 새로운 세그먼트로 추가되어 검색 대상에 포함된다. 삭제 연산 같은 경우는 삭제 여부를 표시하는 비트 배열을 찾아 삭제 여부만 표시하고 끝낸다. 즉, 바로 삭제되는 것이 아니라 단순히 삭제플래그만 남기고 검색 대상에서 제외만 시킨다. 이후 병합 과정이 시작된다면 비로소 삭제플래그값을 가진 데이터가 삭제되는 것이다. 기타 루씬의 Flush, Commit, Merge 작업관련 내용은 아래 링크를 참조하자.

 

 

Lucene - 인메모리버퍼(In-Memory-Buffer) 역할, 세그먼트 병합(Merge)

루씬은 색인 요청이 올때마다 새로운 세그먼트가 추가된다. 그리고 일정한 주기로 세그먼트들을 병합하는 과정을 갖는다. 만약 이러한 루씬에 인메모리버퍼가 하는 역할은 무엇일까? 우선 인메모리버퍼가 없는 루..

coding-start.tistory.com

 

고가용성을 위한 Translog

엘라스틱서치는 분산 시스템이 지원해야 하는 고가용성을 제공하기 위해 내부적으로 Translog라는 특수한 형태의 파일을 유지하고 관리하고 있다. 장애 복구를 위한 백업 데이터 및 데이터 유실 방지를 위한 저장소로써 Tranlog를 활용한다. 분산 코디네이터인 Zookeeper도 클러스터의 고가용성을 위하여 Data Directory에 Translog역할과 거의 동일한 log파일을 작성한다.

 

엘라스틱서치 샤드는 내부에 Translog라는 특수한 파일을 가지고 있다. 샤드에 데이터 변경사항이 생길 경우 Translog 파일에 먼저 해당 내역을 기록한 후 내부에 존재하는 루씬 인덱스로 데이터를 전달한다. 루씬으로 전달된 데이터는 인메모리 버퍼로 저장되고 주기적으로 처리되어 결과적으로 세그먼트가 된다. 엘라스틱서치에서는 기본적으로 1초에 한번씩 Refresh(루씬의 Flush) 작업이 수행되는데, 이를 통해 추가된 세그먼트의 내용을 읽을 수 있게 되고 검색에 사용된다. 하지만 해당 작업이 일어나더라도 Translog파일에 기록된 내용은 삭제되지 않고 계속 유지된다. 이처럼 Translog는 엘라스틱서치 샤드에 일어나는 모든 변경사항을 담고 있는 특수한 형태의 로그인 것이다. 이러한 특성을 이용해 엘라스틱서치는 Tranlog의 내역을 바탕으로 장애복구를 수행한다.

하지만 Translog 파일에 로그가 계속해서 누적될 수는 없다. 특정 시점이 되면 Tranlog 내부의 로그중 불필요한 과거의 로그는 삭제된다. 이 특정 시점은 엘라스틱서치의 Flush(루씬의 Commit)이 수행될때이다. 엘라스틱서치의 Flush는 내부적으로 fsync() 함수를 이용해 실제 물리적인 디스크에 변경 내역을 기록한다. 그리고 작업이 성공적으로 마무리되고 물리적으로 디스크 동기화에 성공하면 누적되어 있던 Tranlog 파일의 내용이 비로소 삭제된다. Flush가 일어난다는 것은 디스크에 물리적으로 기록된다는 것이고 이는 영구적으로 보관된다는 것을 의미하기 때문에 이 시점까지의 로그는 더는 필요하지 않게 된다.

 

그렇다면 엘라스틱서치에서 Translog가 장애복구를 위해서 필요한 것은 알겠지만 구체적으로 어떻게 장애복구에 이용될까? 예를 한번 들어보자. 엘라스틱서치는 1초 마다 Refresh(루씬의 Flush)를 한다. 하지만 이는 물리적인 디스크에 쓰여진 상태가 아니기에 불안정하기에 주기적으로 Flush(루씬의 Commit)를 해야한다. 하지만 Flush 작업은 매우 무겁고 긴 시간 동안 일어날 수 있다.

 

[상황 1]

엘라스틱서치의 Flush에 의해 루씬 Commit 작업이 시작됐고 완료되지 못한 상태에서 샤드에 장애발생.

[해결방법]

샤드가 강제로 종료될 경우 진행 중이던 루씬 Commit 작업이 롤백되기 때문에 샤드가 정상적으로 재실행되면 Translog의 로그 내역을 이용해 간단히 복구 가능하다. 왜냐하면 엘라스틱서치의 Flush가 완료되지 않아서 Translog는 아직 삭제되지 않은 상태이기 때문이다.

 

[상황 2]

변경사항이 순간적으로 많아져서 루씬 Commit이 긴 시간 동안 일어나게 되고 그동안 많은 데이터 변경 요청이 한꺼번에 샤드로 들어옴.

[해결방법]

루씬 Commit 작업이 수행되는 시간이 길어진다고 해서 Commit이 일어나는 동안 샤드로 전달된 변경사항이 Commit 작업이 끝날때까지 반영이 되지 않는다면 실시간 검색을 지원한다는 의미가 거의 없다. 그래서 엘라스틱서치는 Commit이 일어나는 동안 들어온 변경사항을 루씬의 인메모리 버퍼로 전달하지 않고 Translog에 임시로 저장해두고 다음 Commit에 반영될 때까지 유지한다.

 

이러한 Translog는 클러스터 장애복구에 아주 중요한 역할을 하는 로그 파일이다. 하지만 이러한 Translog도 파일이 커지면 복구하는데 장애유발을 할 수 있으므로 적절한 주기로 엘라스틱서치의 Flush(루씬 Commit)를 하여서 적절한 크기의 이하의 Translog 파일을 유지하는 것도 중요하다.

posted by 여성게
: