Qdrant 포인트
Qdrant 포인트 (Points)
Qdrant가 다루는 핵심 단위가 바로 포인트(point)예요. 포인트는 벡터와 선택적인 페이로드로 이루어진 레코드로, 컬렉션에 묶여 벡터 유사도 기반으로 검색돼요. 이 페이지는 포인트의 구조와 ID부터 업로드·수정·삭제·배치 업데이트까지 전 과정을 설명해요.
본문
포인트는 벡터와 선택적인 페이로드로 구성돼요. 가장 단순한 형태는 이렇게 생겼어요.
// This is a simple point
{
"id": 129,
"vector": [0.1, 0.2, 0.3, 0.4],
"payload": {"color": "red"},
}
포인트를 수정하는 모든 작업은 비동기로 이뤄지고 2단계로 진행돼요. 먼저 작업이 Write-ahead-log에 기록되고, 이 시점부터 전원이 끊겨도 데이터를 잃지 않아요.
포인트 ID
Qdrant는 포인트 식별자로 64비트 부호 없는 정수와 UUID를 모두 지원해요.
UUID 문자열 표현 예시:
- simple:
936DA01F9ABD4d9d80C702AF85C822A8 - hyphenated:
550e8400-e29b-41d4-a716-446655440000 - urn:
urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4
즉 모든 요청에서 숫자 ID 대신 UUID 문자열을 쓸 수 있어요. 두 방식 모두 가능해요.
벡터
Qdrant의 각 포인트는 하나 이상의 벡터를 가질 수 있어요. 벡터는 Qdrant 아키텍처의 핵심 구성 요소이며, 서로 다른 유형의 데이터 탐색·검색을 제공하기 위해 다양한 벡터 타입을 써요.
지원되는 벡터 타입:
| 벡터 타입 | 설명 |
|---|---|
| Dense Vectors | 대부분의 임베딩 모델이 생성하는 일반 벡터 |
| Sparse Vectors | 길이가 고정되지 않고 일부 요소만 0이 아닌 벡터. 정확한 토큰 매칭과 협업 필터링 추천에 유용 |
| MultiVectors | 길이가 고정되지만 높이가 가변인 숫자 행렬. ColBERT 같은 late interaction 모델에서 주로 얻음 |
하나의 포인트에 한 종류 이상의 벡터를 붙일 수 있고, Qdrant에서는 이를 Named Vectors라고 불러요.
포인트 업로드
성능 최적화를 위해 Qdrant는 포인트를 배치로 로드할 수 있어요. 한 번의 API 호출로 여러 포인트를 넣을 수 있어 네트워크 연결 생성 오버헤드를 줄여줘요.
Qdrant API는 배치 생성 방식으로 record-oriented와 column-oriented 두 가지를 지원해요. 내부적으로는 동일하며 편의를 위해 제공돼요.
Python 클라이언트 최적화
Python 클라이언트는 포인트 로딩을 위한 추가 기능을 제공해요.
- 병렬화(Parallelization)
- 재시도 메커니즘
- 지연 배치(Lazy batching) 지원
예를 들어 모든 데이터를 RAM에 저장하지 않고 하드디스크에서 직접 읽을 수 있어요. upload_collection과 upload_points 메서드로 이 기능들을 쓸 수 있고, 기본 upsert API처럼 record-oriented와 column-oriented 형식을 모두 지원해요.
upload_points는 v1.7.1부터 사용 가능하며, deprecated된 upload_records를 대체했어요.
Column-oriented 형식:
client.upload_collection(
collection_name="{collection_name}",
ids=[1, 2],
payload=[
{"color": "red"},
{"color": "green"},
],
vectors=[
[0.9, 0.1, 0.1],
[0.1, 0.9, 0.1],
],
parallel=4,
max_retries=3,
)
ids를 제공하지 않으면 Qdrant 클라이언트가 자동으로 랜덤 UUID를 생성해요.
Record-oriented 형식:
client.upload_points(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
payload={
"color": "red",
},
vector=[0.9, 0.1, 0.1],
),
models.PointStruct(
id=2,
payload={
"color": "green",
},
vector=[0.1, 0.9, 0.1],
),
],
parallel=4,
max_retries=3,
)
멱등성 (Idempotence)
포인트 로딩을 포함한 Qdrant의 모든 API는 멱등성이에요. 같은 메서드를 여러 번 실행해도 한 번 실행한 것과 동일하다는 뜻이에요. 즉 같은 id의 포인트를 다시 업로드하면 덮어써져요.
이 멱등성 속성은 exactly-once를 보장하지 않는 메시지 큐를 쓸 때 유용해요. 그런 시스템에서도 Qdrant는 데이터 일관성을 보장해줘요.
업데이트 모드 (v1.17.0+)
기본적으로 upsert는 포인트가 없으면 삽입하고 있으면 업데이트해요. 이 동작을 바꾸려면 update_mode 파라미터를 써요.
upsert(기본값): 포인트가 없으면 삽입, 있으면 업데이트insert_only: 포인트가 없을 때만 삽입. 같은 ID의 포인트가 있으면 무시update_only: 포인트가 이미 있을 때만 업데이트. 없는 포인트는 삽입하지 않음
insert_only 모드는 한 임베딩 모델에서 다른 모델로 마이그레이션할 때 특히 유용해요. 일반 업데이트와 백그라운드 재임베딩 작업 사이의 충돌을 해결해야 하는 상황에서요.
update_only 모드는 조건부 업데이트(conditional updates)와 함께 쓸 때 유용해요. upsert는 없는 포인트에 대해 삽입을 기본으로 하기 때문에, 조건이 충족되지 않아도 update_mode 없이 조건부 업데이트를 하면 새 포인트를 삽입해버릴 수 있어요.
Named Vectors (v0.10.0+)
컬렉션이 여러 벡터로 생성된 경우 각 벡터 데이터를 벡터 이름으로 지정할 수 있어요.
Named vectors는 선택 사항이에요. 포인트 업로드 시 일부 벡터를 생략할 수 있어요. 예를 들어 한 포인트에는 image 벡터만, 다른 포인트에는 text 벡터만 업로드할 수 있어요.
기존 ID의 포인트를 업로드할 때는 기존 포인트를 먼저 삭제한 뒤 지정된 벡터만으로 삽입돼요. 즉 포인트 전체가 교체되고, 지정하지 않은 벡터는 null로 설정돼요. 기존 벡터는 유지하고 지정한 벡터만 업데이트하려면 update vectors를 참고하세요.
Sparse Vectors (v1.7.0+)
포인트는 dense와 sparse 벡터를 모두 포함할 수 있어요.
Sparse 벡터는 대부분의 요소 값이 0인 배열이에요. 이 특성을 활용해 최적화된 표현을 가질 수 있고, 그래서 dense 벡터와 모양이 달라요. (index, value) 쌍의 목록으로 표현되는데, index는 벡터에서 0이 아닌 값의 위치이고 values는 그 값들이에요.
예를 들어 다음 벡터:
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0]
는 sparse 벡터로 이렇게 나타낼 수 있어요:
[(6, 1.0), (7, 2.0)]
Qdrant는 API 전반에서 다음 JSON 표현을 사용해요.
{
"indices": [6, 7],
"values": [1.0, 2.0]
}
indices와 values 배열의 길이는 같아야 하고, indices는 고유해야 해요. 인덱스가 정렬되어 있지 않으면 Qdrant가 내부적으로 정렬하므로 요소 순서에 의존하면 안 돼요.
Sparse 벡터는 반드시 이름을 가져야 하며 dense 벡터와 같은 방식으로 업로드할 수 있어요.
인퍼런스 (Inference)
벡터를 명시적으로 제공하는 대신, Qdrant가 inference라는 과정으로 벡터를 생성할 수도 있어요. Inference는 머신러닝 모델로 텍스트·이미지·기타 데이터 유형에서 벡터 임베딩을 만드는 과정이에요.
일반 벡터를 쓸 수 있는 어디든 inference를 사용할 수 있어요. 포인트 업서트 중에 텍스트나 이미지와 임베딩 모델을 제공하면 Qdrant가 모델로 임베딩을 생성하고 결과 벡터와 함께 포인트를 저장해요.
포인트 수정
포인트의 벡터나 페이로드를 수정할 수 있어요.
벡터 업데이트 (v1.2.0+)
지정한 포인트의 지정 벡터만 업데이트하고, 지정하지 않은 벡터는 그대로 유지해요. 주어진 포인트는 모두 존재해야 해요. 모든 벡터를 교체하려면 포인트 업로드를 참고하세요.
벡터 삭제 (v1.2.0+)
지정한 포인트에서 특정 벡터만 삭제하고, 다른 벡터는 유지해요. 포인트 자체는 삭제되지 않아요. 포인트 전체를 삭제하려면 포인트 삭제를 참고하세요.
페이로드 업데이트
포인트의 페이로드를 수정하는 방법은 Payload 섹션에서 다뤄요.
포인트 삭제
삭제할 포인트를 지정하는 또 다른 방법은 필터를 쓰는 것이에요. 예를 들어 컬렉션에서 { "color": "red" }인 모든 포인트를 제거할 수 있어요.
조건부 업데이트 (v1.16.0+)
order_by 파라미터를 쓰면 페이지네이션이 비활성화돼요. 정렬 값이 고유하지 않을 때는 ID 오프셋에 의존할 수 없어서 응답에 next_page_offset이 반환되지 않아요. 하지만 "order_by": { "start_from": ... }와 { "must_not": [{ "has_id": [...] }] } 필터를 조합하면 여전히 페이지네이션을 할 수 있어요.
포인트 개수 세기 (v0.8.4+)
실제 검색 없이 필터 조건에 맞는 포인트 수를 알면 유용할 때가 있어요.
- 패싯 검색 결과 크기 평가
- 페이지네이션 페이지 수 결정
- 쿼리 실행 속도 디버깅
필터 조건에 맞는 포인트 수를 반환해요:
{
"count": 3811
}
기본적으로 성능을 위해 개수는 근사값이에요. 정확한 개수를 얻으려면 요청에서 "exact": true로 설정해요.
배치 업데이트 (v1.5.0+)
여러 포인트 업데이트 작업을 배치로 묶을 수 있어요. 포인트·벡터·페이로드의 삽입·업데이트·삭제가 모두 포함돼요.
배치 업데이트 요청은 작업 목록으로 구성되며 순서대로 실행돼요. 배치할 수 있는 작업:
- 포인트 업서트:
upsert또는UpsertOperation - 포인트 삭제:
delete_points또는DeleteOperation - 벡터 업데이트:
update_vectors또는UpdateVectorsOperation - 벡터 삭제:
delete_vectors또는DeleteVectorsOperation - 페이로드 설정:
set_payload또는SetPayloadOperation - 페이로드 덮어쓰기:
overwrite_payload또는OverwritePayload - 페이로드 키 삭제:
delete_payload또는DeletePayloadOperation - 페이로드 비우기:
clear_payload또는ClearPayloadOperation
많은 포인트를 단일 작업 유형으로 배치하려면 해당 작업의 배치 기능을 직접 사용해요.
결과 대기 (Awaiting Result)
&wait=false로 API를 호출하거나 명시하지 않으면 클라이언트는 데이터 수신 확인(acknowledgement)만 받아요:
{
"result": {
"operation_id": 123,
"status": "acknowledged"
},
"status": "ok",
"time": 0.000206061
}
이 응답은 데이터가 아직 조회 가능하다는 뜻이 아니에요. 컬렉션 업데이트는 백그라운드에서 일어나므로 실제로 처리되기까지 짧은 시간이 걸릴 수 있고, 최종 실패할 가능성도 있어요. 이는 일종의 eventual consistency를 사용하는 방식이에요. 벡터를 많이 삽입하는 경우 파이프라이닝을 활용하도록 비동기 요청을 사용하는 것도 권장해요.
애플리케이션 로직상 API 응답 직후 벡터가 검색에 반드시 보여야 한다면 ?wait=true 플래그를 써요. 이 경우 작업이 끝난 뒤에만 결과를 반환해요:
{
"result": {
"operation_id": 0,
"status": "completed"
},
"status": "ok",
"time": 0.000206061
}