Search-Engine/Lucene 2019. 5. 25. 21:04

루씬은 색인 요청이 올때마다 새로운 세그먼트가 추가된다. 그리고 일정한 주기로 세그먼트들을 병합하는 과정을 갖는다. 만약 이러한 루씬에 인메모리버퍼가 하는 역할은 무엇일까? 우선 인메모리버퍼가 없는 루씬을 가정한다면, 만약 순간적으로 대용량의 데이터의 색인요청이 많아질 경우 세그먼트(역색인 파일)의 개수가 너무 많아져서 문제가 될 수 있다. 파일이 갑자기 많아지고 이는 당연히 색인에 지연이 생길 것이고 최종적으로 서비스 장애로 이어질 것이다. 하지만 실제적으로 루씬은 색인 작업이 요청되면 전달된 데이터는 일단 인메모리버퍼에 순서대로 쌓이고 버퍼가 일정크기 이상의 데이터가 쌓였다면 그때 한번에 모아서 색인처리를 한다. 즉, 버퍼가 일종의 큐역할을 하는 것이다. 버퍼에 모여 한번에 처리된 데이터는 즉시 세그먼트 형태로 생성되고 디스크로 동기화된다. 하지만 디스크에 물리적으로 동기화하는 일련의 과정은 운영체제 입장에서 비용이 큰 연산이기에 세그먼트가 생성될때마다 물리적인 동기화를 할 경우 성능이 급격히 나빠질 수 있다. 루씬은 이러한 문제점을 해결하기 위해 무거운 fsync 방식을 이용해 디스크 동기화를 하는 대신 상대적으로 가벼운 write 방식을 이용해 쓰기 과정을 수행한다. 이러한 방식으로 쓰기 성능을 높이고 이후 일정한 주기에 따라 물리적인 디스크 동기화 작업을 수행한다.

 

write() 함수

일반적으로 파일을 저장할 때 사용하는 함수다. 운영체제 내부 커널에는 시스템 캐시가 존재하는데 write() 함수를 이용하면 일단 시스템 캐시에만 기록되고 리턴된다. 이후 실제 데이터는 특정한 주기에 따라 물리적인 디스크로 기록된다. 물리적인 디스크 쓰기 작업을 수행하지 않기 때문에 빠른 처리가 가능한 반면 최악의 경우 시스템이 비정상 종료될 경우에는 데이터 유실이 일어날 수도 있다.

 

fsync() 함수

저수준의 파일 입출력 함수다. 내부 시스템 캐시의 데이터와 물리적인 디스크의 데이터를 동기화하기 위한 목적으로 사용된다. 실제 물리적인 디스크로 쓰는 작업을 수행하기 때문에 상대적으로 많은 리소스가 소모된다.

 

이러한 인메모리 버퍼 기반의 처리 과정을 루씬에서는 Flush라고 부른다. 데이터의 변경사항을 일단 버퍼에 모아두었다가 일정 주기에 한번씩 세그먼트를 생성하고 상대적으로 낮은 비용으로 디스크에 동기화 하는 작업까지 수행한다. 일단 Flush 처리에 의해 세그먼트가 생성되면 커널 시스템 캐시에 세그먼트가 캐시되어 읽기가 가능해진다. 커널 시스템 캐시에 캐시가 생성되면 루씬의 openIfChanged()을 이용해 IndexSearcher에서도 읽을 수 있는 상태가 된다.

 

openIfChanged() 함수

루씬에서는 IndexSearcher 객체가 생성되고 나면 이후 변경된 사항들을 기본적으로 인지하지 못한다. 기존 IndexSearcher를 Close하고 다시 생성하면 변경된 사항을 인지하는 것이 가능하지만 문서의 추가나 변경이 빈번하게 일어날 경우 많은 리소스가 필요해지기 때문에 권장하지 않는다. 이때 사용하는 것이 openIfChanged() 함수다. 일정 주기마다 문서가 업데이트된다면 openIfChanged()함수를 이용해 좀더 효율적으로 리소스를 사용할 수 있다.

 

하지만 최악의 경우에는 Flush만으로는 100% 데이터의 유실을 보장할 수 없다고 했다. 즉, fsync() 함수를 이용하여 언젠가는 반드시 동기화를 해야한다. 이러한 작업을 Commit이라고 한다. 매번 Commit하는 것이 아니고 Flush 작업을 몇번 한 이후에 일정 주기로 Commit작업을 통해 물리적인 디스크로 기록 작업을 수행해야한다.

 

아무리 루씬이 세그먼트 단위 검색을 지원하지만 시간이 지날수록 세그먼트 수가 많아지면 커밋 포인트의 부하도 증가하고 여러개의 세그먼트를 검색해야하기 때문에 검색 성능도 저하된다.그래서 일정주기 동안 여러개의 세그먼트는 하나의 세그먼트로 병합이 된다. 이러한 병합 처리에 여러 장점이 존재한다.

 

 

병합의 장점

  • 검색 성능 향상 : 검색 요청이 들어오면 루씬 내부에 존재하는 모든 세그먼트를 검색해야하는데, 각 세그먼트는 순차적으로 검색되므로 세그먼트를 병합하여 세그먼트 수를 줄이면 순차 검색 횟수도 줄어든다.
  • 디스크 용량 최소화 : 삭제되는 문서의 경우 병합 작업 전에는 삭제 플래그 값을 가지고 삭제되지 않고 물리적인 디스크에 남아있는다. 이러한 삭제 플래그를 가진 문서는 병합 작업을 시작해야 비로소 삭제된다.

 

이러한 병합 작업은 Commit 작업을 반드시 동반해야한다.

 

루씬 Flush 작업

  • 세그먼트가 생성된 후 검색이 가능해지도록 수행하는 작업
  • write() 함수로 동기화가 수행됬기 때문에 커널 시스템 캐시에만 데이터가 생성된다.이를 통해 유저 모드에서 파일을 열어 사용하는 것이 가능해진다.
  • 물리적으로 디스크에 쓰여진 상태는 아니다.

루씬 Commit 작업

  • 커널 시스템 캐시의 내용을 물리적인 디스크로 쓰는 작업
  • 실제 물리적인 디스크에 데이터가 기록되기 때문에 많은 리소스 필요

루씬 Merge 작업

  • 다수의 세그먼트를 하나로 통합하는 작업
  • Merge 과정을 통해 삭제 플래그 값을 가진 데이터가 실제 물리적으로 삭제 처리된다.
  • 검색할 세그먼트의 수가 줄어들기 때문에 검색 성능이 향상된다.

 

posted by 여성게
: