Bối cảnh & Lý do: Chuyển dịch Vector Workload sang Rust
Sáu tháng trước, đội ngũ của chúng tôi gặp nút thắt lớn với hạ tầng semantic search. Chúng tôi vận hành các luồng RAG phục vụ trực tiếp người dùng trên 20 triệu chunk được embed bằng các model 1536 chiều của OpenAI. Hệ thống cũ—PostgreSQL kết hợp pgvector chạy trên instance AWS r6i.4xlarge—bị quá tải khi lưu lượng tăng cao. Trong các đợt re-indexing số lượng lớn, độ trễ tìm kiếm p99 tăng vọt từ 45ms lên hơn 140ms. Tệ hơn nữa, Postgres ngốn hơn 160GB RAM, liên tục mấp mé nguy cơ crash do out-of-memory.
Các cơ sở dữ liệu quan hệ và document store vẫn rất tuyệt vời cho các bản ghi giao dịch có cấu trúc. Tuy nhiên, tìm kiếm vector đa chiều đòi hỏi cách bố trí bộ nhớ chuyên biệt và tăng tốc phần cứng. Chúng tôi đã đánh giá Milvus, Pinecone và Qdrant trước khi quyết định chuẩn hóa toàn bộ trên Qdrant.
Codebase viết bằng Rust của Qdrant đã loại bỏ hoàn toàn hiện tượng tạm dừng do garbage collection. Khả năng tăng tốc SIMD (AVX-512) giúp tối đa hóa thông lượng tính toán trên chip x86. Sau khi migration, độ trễ truy vấn p99 của chúng tôi giảm xuống còn 11.8ms ở mức 450 QPS. Mức sử dụng RAM giảm tới 68% sau khi kích hoạt scalar quantization.
Cài đặt: Thiết lập Docker & Docker Compose chuẩn Production
Các instance đơn lẻ dạng ad-hoc thường sớm thất bại trong môi trường production. Bạn cần khả năng lưu trữ đĩa dự đoán được, giới hạn tài nguyên rõ ràng và các health check tự động được cấu hình qua Docker Compose.
Cấu trúc thư mục dự án
mkdir -p /opt/qdrant/{data,config,snapshots}
cd /opt/qdrant
Cấu hình Docker Compose
version: '3.8'
services:
qdrant:
image: qdrant/qdrant:v1.8.4
container_name: qdrant_production
restart: unless-stopped
ports:
- "6333:6333" # REST API & Giao diện Web
- "6334:6334" # gRPC API
environment:
- QDRANT__SERVICE__HTTP_PORT=6333
- QDRANT__SERVICE__GRPC_PORT=6334
- QDRANT__SERVICE__ENABLE_CORS=true
- QDRANT__SERVICE__API_KEY=${QDRANT_API_KEY}
volumes:
- ./data:/qdrant/storage
- ./config/config.yaml:/qdrant/config/production.yaml:ro
- ./snapshots:/qdrant/snapshots
ulimits:
nofile:
soft: 65535
hard: 65535
deploy:
resources:
limits:
cpus: '8.0'
memory: 32G
reservations:
cpus: '4.0'
memory: 16G
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:6333/healthz || exit 1"]
interval: 15s
timeout: 5s
retries: 3
start_period: 10s
Tạo API key 256-bit và ghi vào file .env trước khi khởi chạy container daemon:
echo "QDRANT_API_KEY=$(openssl rand -hex 32)" > .env
docker compose up -d
Cấu hình: Tối ưu hóa Storage, HNSW và Quantization
Cấu hình mặc định ban đầu thường ưu tiên tạo mẫu nhanh cục bộ hơn là thông lượng lập chỉ mục. Với các collection chứa hàng chục triệu vector, hãy tinh chỉnh ngưỡng indexing và chuyển JSON payload xuống ổ đĩa NVMe.
File config/config.yaml cho Production
storage:
storage_path: /qdrant/storage
snapshots_path: /qdrant/snapshots
on_disk_payload: true
service:
max_request_size_mb: 32
max_workers: 8
hnsw_index:
m: 16
ef_construct: 128
full_scan_threshold: 10000
max_indexing_threads: 4
on_disk: false
Cấu hình Scalar Quantization để tối ưu bộ nhớ
Vector dạng floating-point 32-bit nguyên bản tốn rất nhiều chi phí khi mở rộng quy mô. 20 triệu vector 1536 chiều cần khoảng 122.8 GB RAM chỉ riêng cho dữ liệu vector (20,000,000 * 1536 * 4 bytes). Kích hoạt scalar quantization 1-byte (int8) giúp nén dung lượng này xuống chỉ còn ~30.7 GB. Trong các đợt đánh giá recall với tập kiểm thử 10.000 truy vấn mẫu, độ chính xác chỉ giảm 0.8% trong khi thông lượng tăng gấp đôi.
Dưới đây là script khởi tạo collection cho production bằng Python client chính thức:
import os
from qdrant_client import QdrantClient
from qdrant_client.http import models
client = QdrantClient(
url="http://localhost:6333",
api_key=os.getenv("QDRANT_API_KEY"),
prefer_grpc=True
)
# Khởi tạo collection với scalar quantization
client.create_collection(
collection_name="knowledge_base",
vectors_config=models.VectorParams(
size=1536,
distance=models.Distance.COSINE,
on_disk=False # Giữ graph trong RAM để duyệt nhanh nhất
),
optimizers_config=models.OptimizersConfigDiff(
indexing_threshold=20000,
memmap_threshold=50000
),
quantization_config=models.ScalarQuantization(
scalar=models.ScalarQuantizationConfig(
type=models.ScalarType.INT8,
quantile=0.99,
always_ram=True
)
)
)
# Đánh chỉ mục các trường metadata để lọc boolean/equality nhanh chóng
client.create_payload_index(
collection_name="knowledge_base",
field_name="tenant_id",
field_schema=models.PayloadSchemaType.KEYWORD
)
client.create_payload_index(
collection_name="knowledge_base",
field_name="created_at",
field_schema=models.PayloadSchemaType.INTEGER
)
Kiểm thử & Giám sát: Health Check và Metric Production
Sau khi dựng xong cluster, hãy xác thực cơ chế nạp dữ liệu (ingestion) và truy vấn trước khi điều hướng traffic thực tế từ người dùng.
1. Kiểm thử Batch Upsert và Tìm kiếm có bộ lọc
Xác thực các truy vấn semantic theo từng tenant trực tiếp bằng Python:
# Chèn điểm dữ liệu (point) tài liệu mẫu
client.upsert(
collection_name="knowledge_base",
points=[
models.PointStruct(
id="c56a4180-65aa-42ec-a945-5fd21dec0538",
vector=[0.012] * 1536,
payload={
"tenant_id": "tenant_acme_corp",
"doc_title": "Kiến trúc Qdrant Production",
"created_at": 1717200000
}
)
]
)
# Truy vấn kết hợp pre-filtering
search_hits = client.query_points(
collection_name="knowledge_base",
query=[0.012] * 1536,
query_filter=models.Filter(
must=[
models.FieldCondition(
key="tenant_id",
match=models.MatchValue(value="tenant_acme_corp")
)
]
),
limit=5,
with_payload=True
)
for hit in search_hits.points:
print(f"Điểm số: {hit.score:.4f} | Tiêu đề: {hit.payload['doc_title']}")
2. Tích hợp Metric Prometheus
Qdrant cung cấp các metric open telemetry qua HTTP tại endpoint /metrics. Bạn có thể tích hợp trực tiếp vào scraper trung tâm:
scrape_configs:
- job_name: 'qdrant'
metrics_path: '/metrics'
bearer_token: 'YOUR_QDRANT_API_KEY'
static_configs:
- targets: ['qdrant_production:6333']
3. Các Metric quan trọng cần theo dõi
app_telemetry_rest_requests_duration_seconds_bucket: Theo dõi phân phối độ trễ tìm kiếm. Cần kích hoạt cảnh báo nếu p99 vượt quá 35ms trong khung thời gian trượt 5 phút.collections_vector_count: Tổng số bản ghi vector trong mỗi collection. Dùng để theo dõi xu hướng tăng trưởng dữ liệu.process_resident_memory_bytes: Bộ nhớ được cấp phát trực tiếp bởi tiến trình. Hãy đặt cảnh báo ở mức 80% giới hạn RAM của container để tránh bị Linux OOM kill.cluster_pending_operations: Độ sâu hàng đợi của optimizer chưa được xử lý. Việc chỉ số này tăng đột biến liên tục cho thấy tiến trình nén index (index compaction) đang không theo kịp khối lượng ghi hàng loạt.
Bài học vận hành sau 6 tháng hoạt động liên tục
Ba mô hình thực tế đã giúp hạ tầng của chúng tôi duy trì sự ổn định:
- Bắt buộc phải đánh chỉ mục payload (Payload indexing): Luôn tạo payload index riêng cho các trường phân tách tenant hoặc timestamp. Lọc trên các trường chưa đánh index sẽ ép hệ thống phải scan toàn bộ segment, làm suy giảm nghiêm trọng khả năng xử lý truy vấn đồng thời.
- Chuyển pipeline nạp dữ liệu sang gRPC: Gửi vector qua gRPC ở cổng 6334 đã giúp giảm 28% mức sử dụng CPU khi nạp và tăng thông lượng ghi từ 1.800 lên 4.200 points/giây trên mỗi worker.
- Tự động hóa việc luân chuyển snapshot: Tạo point-in-time snapshot hàng đêm qua API
/collections/{name}/snapshots, nén lại và đồng bộ lên S3 cold bucket. Quá trình tạo snapshot là lock-free và không bao giờ làm gián đoạn lưu lượng đọc.

