각 데이터 컬렉션은 세그먼트로 나누어집니다. 각 세그먼트에는 독립적인 벡터 저장, 페이로드 저장 및 인덱스가 있습니다.

일반적으로 세그먼트의 데이터는 중첩되지 않습니다. 그러나 동일한 지점을 다른 세그먼트에 저장해도 문제가 발생하지 않습니다. 왜냐하면 검색에 중복 제거 메커니즘이 포함되기 때문입니다.

세그먼트에는 벡터 저장, 페이로드 저장, 벡터 인덱스, 페이로드 인덱스, 내부 및 외부 ID 간 관계를 저장하는 ID 맵퍼가 포함됩니다.

저장소 및 인덱스 유형에 따라 세그먼트는 "추가 가능" 또는 "추가 불가능"할 수 있습니다. "추가 가능" 세그먼트에서는 데이터를 자유롭게 추가, 삭제 및 쿼리할 수 있지만, "추가 불가능" 세그먼트에서는 데이터를 읽거나 삭제할 수만 있습니다.

컬렉션의 세그먼트 구성은 서로 다르고 독립적일 수 있으나, 적어도 하나의 "추가 가능" 세그먼트가 필요합니다.

벡터 저장

응용 프로그램의 요구에 따라 Qdrant는 다음 데이터 저장 옵션 중 하나를 사용할 수 있습니다. 이 선택은 검색 속도와 RAM 사용량 사이의 균형을 유지해야 합니다.

메모리 저장 - 모든 벡터를 RAM에 저장하여 디스크 액세스가 지속되는 동안에만 제공되므로 최고의 속도를 제공합니다.

Memmap 저장 - 디스크에 연결된 파일과 관련된 가상 주소 공간(virtual address space)을 생성합니다. 매핑된 파일은 직접적으로 RAM에 로드되지 않지만 페이지 캐싱을 이용하여 액세스됩니다. 이 접근 방식은 사용 가능한 메모리를 유연하게 사용할 수 있습니다. 충분한 RAM이 있다면 메모리 저장처럼 거의 빠른 속도를 제공합니다.

Memmap 저장 구성

메모리 매핑(memap) 사용을 구성하는 두 가지 방법이 있습니다:

  • 컬렉션 생성 API에서 벡터에 대한 on_disk 옵션 설정:

1.2.0 버전 이상에서만 적용 가능

PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosine",
      "on_disk": true
    }
}

이렇게 하면 모든 벡터를 즉시 메모리 맵 저장소에 저장하는 컬렉션이 생성됩니다. Qdrant 인스턴스가 빠른 디스크를 사용하고 대규모 컬렉션을 처리해야 할 때 이 방법을 권장합니다.

  • memmap_threshold_kb 옵션 설정. 이 옵션은 세그먼트를 memmap 저장소로 변환하는 임계값을 설정합니다.

이를 구현하는 두 가지 방법이 있습니다:

  1. 구성 파일에서 전역적으로 임계 값을 설정합니다. 매개변수 이름은 memmap_threshold_kb입니다.
  2. 생성 또는 업데이트 중에 각 컬렉션별로 임계 값을 개별적으로 설정합니다.
PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosine"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    }
}

memmap 임계값 매개변수를 설정하는 규칙은 간단합니다:

  • 사용 시나리오가 균형적인 경우 - memmap 임계값을 indexing_threshold와 동일하게 설정합니다 (기본값은 20000). 이 경우 최적화 프로그램은 추가적인 실행을 수행하지 않고 한꺼번에 모든 임계값을 최적화합니다.
  • 쓰기 부하가 높고 RAM이 적은 경우 - memmap 임계값을 indexing_threshold보다 낮게 설정합니다. 예를 들어 10000입니다. 이 경우 최적화 프로그램은 우선 세그먼트를 memmap 저장으로 변환한 후에 인덱싱을 적용합니다.

또한 memmap 저장을 벡터 뿐만 아니라 HNSW 인덱스에도 사용할 수 있습니다. 이 기능을 활성화하려면 컬렉션 생성 시 hnsw_config.on_disk 매개변수를 true로 설정합니다.

PUT /collections/{collection_name}

{
    "vectors": {
      "size": 768,
      "distance": "Cosine"
    },
    "optimizers_config": {
        "memmap_threshold": 20000
    },
    "hnsw_config": {
        "on_disk": true
    }
}

페이로드 저장

Qdrant는 두 가지 유형의 페이로드 저장을 지원합니다: 인메모리 및 온디스크

인메모리 페이로드 저장은 페이로드 데이터를 인메모리 벡터와 동일한 방식으로 구성합니다. 서비스가 시작될 때 페이로드 데이터가 메모리에 로드되며, 디스크와 RocksDB는 영속성에만 사용됩니다. 이 유형의 저장소는 매우 빠르지만, 특히 페이로드에 대량의 데이터(텍스트 요약 또는 이미지와 같은 대형 값)가 포함된 경우 모든 데이터를 저장하기 위해 큰 메모리 공간이 필요할 수 있습니다.

대용량 페이로드 값을 처리할 때는 온디스크 페이로드 저장을 사용하는 것이 좋습니다. 이 유형의 저장소는 페이로드를 직접 RocksDB에 읽고 쓰므로 저장에 큰 메모리가 필요하지 않습니다. 그러나 액세스 지연이 발생할 수 있습니다. 페이로드 조건에 따라 벡터를 쿼리해야 하는 경우 디스크에 저장된 값을 확인하는 데 시간이 오래 걸릴 수 있습니다. 이 경우 디스크 액세스를 피하기 위해 필터링 조건에 사용되는 각 필드에 대해 페이로드 인덱스를 작성하는 것이 좋습니다. 필드 인덱스를 생성하면 Qdrant는 페이로드 저장 유형에 관계없이 인덱싱된 필드의 모든 값을 항상 메모리에 유지합니다.

원하는 페이로드 저장 유형은 설정 파일을 통해 지정하거나, 컬렉션을 만들 때 on_disk_payload 컬렉션 매개변수를 사용하여 지정할 수 있습니다.

버전 관리

데이터 무결성을 보장하기 위해 Qdrant는 모든 데이터 변경을 두 단계에서 수행합니다. 먼저 데이터는 Write-ahead-log (WAL)에 기록되며 모든 작업에 순차적 번호가 할당됩니다.

WAL에 변경 내용이 추가되면 전원이 꺼져도 변경 내용이 유실되지 않습니다. 그 후 변경 내용이 세그먼트에 들어갑니다. 각 세그먼트는 해당 세그먼트에 적용된 변경 사항의 최신 버전과 각 개별 포인트의 버전을 저장합니다. 새 변경의 번호가 포인트의 현재 버전보다 작으면 업데이터는 해당 변경을 무시합니다. 이 메커니즘을 통해 Qdrant는 비정상적인 종료 시 WAL에서 저장소를 효율적으로 복구할 수 있습니다.