Qdrant 옵티마이저
Qdrant 옵티마이저 (Optimizer)
Qdrant는 데이터를 저장할 때 세그먼트(segment) 단위로 나눠 관리하고, 구조를 바꿔야 하면 통째로 다시 빌드해요. 여기서 만약 변경 사항을 하나씩 처리하면 느리기 때문에, 옵티마저가 변경을 모아서 배치 방식으로 구조를 재구성해줘요. 이 페이지는 Qdrant 옵티마이저의 종류와 동작 방식을 정리해요.
본문
Qdrant의 저장소 최적화는 세그먼트 레벨에서 이뤄져요. 최적화되는 동안에도 해당 세그먼트는 계속 읽을 수 있는 상태로 유지되는데, 이는 세그먼트를 프록시(proxy)로 감싸서 데이터 변경을 투명하게 처리하기 때문이에요. 변경된 데이터는 copy-on-write 세그먼트에 쌓이고, 이 세그먘트가 조회와 이후 업데이트에서 우선순위를 가져요.
Vacuum 옵티마이저
삭제된 레코드를 관리해서 저장 공간을 정리해요. 레코드를 삭제하면 바로 없애지 않고 '삭제 표시'만 해두는데, 시간이 지나면 이 표시가 쌓여 메모리를 낭비하고 속도를 떨어뜨릴 수 있어요. Vacuum 옵티마이저는 이렇게 표시만 된 레코드를 영구히 제거하고 저장소를 재정리해서 메모리를 아껴요.
storage:
optimizers:
# 세그먼트 최적화를 수행하기 위해 필요한 삭제 벡터의 최소 비율
deleted_threshold: 0.2
# 세그먼트 최적화를 수행하기 위해 필요한 최소 벡터 수
vacuum_min_vector_number: 1000
deleted_threshold: 세그먼트 안에서 삭제 표시된 레코드의 최소 비율. 예를 들어 0.2는 세그먼트 레코드의 20%가 삭제 표시되면 옵티마이저가 동작한다는 뜻이에요.vacuum_min_vector_number: 최적화 대상이 되려면 세그먼트가 가져야 하는 최소 벡터 수예요. 1000으로 두면 1,000개 이상 벡터를 가진 세그먼트만 최적화해요.
기준을 충족하면 삭제된 레코드를 제거하고 데이터를 재조직해서 쿼리 성능을 높이고 중복 데이터를 없애 메모리 사용을 줄여요.
Merge 옵티마이저
세그먼트 개수를 조절해서 데이터 구성과 검색 성능을 유지해요. 최적화 과정에서 copy-on-write 같은 임시 세그먼트가 생기기도 하는데, 잦은 업데이트를 효율적으로 다루려면 작은 세그먼트가 최소 하나는 필요해요. 하지만 작은 세그먼트가 너무 많으면 검색 성능이 나빠지기 때문에, Merge 옵티마이저가 적정 개수를 넘어가면 세그먼트 수를 줄여요.
대상 세그먼트 수는 default_segment_number로 지정하는데, 기본값은 CPU 코어 수예요. 최적화 중에는 가장 작은 세그먼트 세 개를 하나로 합쳐 세그먼트 크기와 시스템 성능의 균형을 맞춰요. 또 max_segment_size_kb로 세그먼트 크기 상한을 정해서 너무 큰 세그먼트가 생겨 인덱싱이 느려지는 걸 방지해요.
storage:
optimizers:
# 옵티마이저가 유지하려는 목표 세그먼트 수.
# 실제 세그먼트 수는 여러 파라미터에 따라 달라질 수 있어요:
# - 저장된 포인트 수
# - 현재 쓰기 RPS
#
# 각 세그먼트가 스레드 하나에 고르게 처리되도록,
# 검색 스레드 수의 배수로 정하는 걸 권장해요.
# `default_segment_number = 0`이면 사용 가능한 CPU 수로 자동 결정돼요.
default_segment_number: 0
default_segment_number: 세그먼트가 검색 스레드 수와 맞아서 여러 스레드에 고르게 분산되게 해줘요.max_segment_size_kb: 세그먼트 크기를 제한해서 시스템 우선순위에 따라 인덱싱 성능과 검색 성능을 조절해요.
Indexing 옵티마이저
레코드 수에 따라 어떤 인덱스와 저장 방식을 쓸지 결정해요. 예를 들어 포인트가 10,000개보다 적으면 어떤 인덱스를 쓰는 것보다 brute-force 스캔이 더 효율적이에요. Indexing 옵티마이저는 최소 레코드 수에 도달했을 때 인덱스와 memmap 저장 방식을 켜는 역할을 해요. 시작 기준은 설정 파일에서 정해요.
컬렉션별 옵티마이저 설정
옵티마이저 파라미터는 컬렉션 생성 시점이나 이후 업데이트 때 컬렉션별로도 설정할 수 있어요.
큰 미인덱스 세그먼트 읽기 방지
미인덱스 데이터가 많아지면 매 쿼리마다 그 데이터를 스캔해야 해서 검색이 느려질 수 있어요. Qdrant는 indexed_only를 true로 설정해서 인덱스된 데이터만 검색할 수 있게 해줘요. 다만 이렇게 하면 최근에 업데이트된 데이터가 다시 인덱스되기 전까지 검색 결과에서 잠시 사라질 수 있어요('blinking' 포인트).
이를 막기 위해 prevent_unoptimized 모드를 지원해요. 활성화하면 indexing_threshold보다 큰 미인덱스 세그먼트에 쓰인 포인트는 저장은 되지만 검색 결과에선 보이지 않고, 옵티마이저가 세그먼트를 인덱스한 뒤에만 보여요. prevent_unoptimized는 컬렉션별로 또는 설정 파일에서 전역으로 켤 수 있어요.
PATCH /collections/{collection_name}
{
"optimizers_config": {
"prevent_unoptimized": true
}
}
wait=true에 미치는 영향
쓰기 요청은 wait 파라미터를 지원해요. wait=true로 하면 업데이트가 적용돼 검색에 보이는 상태까지 완료된 뒤에 응답을 돌려줘요. prevent_unoptimized를 켜면 응답은 현재 업데이트를 포함한 모든 deferred 포인트가 인덱스되어 검색에 보일 때까지 지연돼요.
deferred 포인트 모니터링
컬렉션의 deferred 포인트 수는 컬렉션 정보 API 응답의 update_queue 섹션에서 확인할 수 있어요.
최적화 모니터링 (v1.17.0+)
/collections/{collection_name}/optimizations 엔드포인트는 특정 컬렉션의 최적화 정보를 반환해요. 대기 중인 최적화/세그먼트/포인트 수와 작업이 필요 없는 idle 세그먼트 요약, 그리고 현재 실행 중인 최적화(어떤 옵티마이저인지, 상태, 관련 세그먼트, 진행률) 정보가 담겨요.
with 쿼리 파라미터로 추가 정보를 받을 수 있어요.
GET /collections/{collection_name}/optimizations?with=queued,completed
웹 UI의 Collections 인터페이스에 있는 Optimizations 탭에서도 같은 정보를 확인할 수 있어요. 특정 최적화 사이클을 선택하면 그 사이클에서 수행된 작업과 소요 시간을 자세히 볼 수 있어요.