Belajar ChromaDB - Scaling & Performance
Episode 17 of 23

Belajar ChromaDB - Scaling & Performance

Episode ini membahas scaling dan performance ChromaDB: melayani jutaan vektor dengan server Rust, batch operations untuk ingestion cepat, tuning index, optimasi ef search, pra-filter metadata, dan sizing resource CPU serta RAM yang tepat.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Pendahuluan

Aplikasi kalian sudah berjalan, data bertambah, dan tiba saatnya pertanyaan yang menakutkan: bisakah ini melayani jutaan vektor? Episode 17 menjawabnya dengan membahas scaling dan performance: bagaimana server Rust menangani volume besar, cara mengingest data secara batch, tuning index, optimasi ef search, pra-filter metadata, dan sizing resource yang benar.

Performance di ChromaDB bukan misteri — ia terdiri dari keputusan-keputusan spesifik yang bisa kalian kendalikan. Mari kita bongkar satu per satu.

Server Rust dan Skala Jutaan Vektor

Kenapa Server Rust Menjadi Dasar Scaling

Server Rust adalah fondasi scaling ChromaDB modern. Dibanding server Python FastAPI, Rust memberikan performa yang lebih deterministik dan penggunaan memori yang lebih efisien — dua hal yang menentukan saat data tumbuh menjadi jutaan vektor.

Server Rust memuat seluruh index HNSW ke memori untuk query cepat. Inilah kenapa sizing RAM adalah keputusan paling penting di bagian ini: index yang muat di RAM jauh lebih cepat daripada yang harus diakses dari disk.

Info

HNSW adalah index in-memory. Setiap penambahan dan query berjalan di RAM. Rule of thumb: sediakan cukup RAM untuk index plus ruang untuk build. Episode ini dan episode 9 saling melengkapi untuk menemukan angka yang tepat.

Batch Operations untuk Ingestion

Menghindari Satu-Per-Satu

Kesalahan performa paling umum: memanggil add dalam loop untuk setiap dokumen. Setiap panggilan punya overhead protokol dan reindex parsial. Solusinya: batch.

PythonIngestion batch (benar)
batch_ids = []
batch_docs = []
batch_meta = []
 
for dok in dokumen_sumber:
    batch_ids.append(buat_id(dok))
    batch_docs.append(dok)
    batch_meta.append({"source": "ingestion"})
 
collection.upsert(
    ids=batch_ids,
    documents=batch_docs,
    metadatas=batch_meta,
)

Contoh di atas mengumpulkan data lalu memanggil collection.upsert(...) sekali untuk seluruh batch. Perbandingan: 10 ribu panggilan kecil versus 1 panggilan besar bisa 10-100 kali lebih cepat untuk yang terakhir.

Ukuran Batch yang Masuk Akal

Ukuran batch ideal tergantung dimensi embedding dan RAM. Titik awal yang umum: 1.000-10.000 item per panggilan. Jangan memaksakan batch raksasa dalam satu memori — bagi menjadi beberapa panggilan 10 ribu jika perlu:

PythonBatch dengan chunk
for i in range(0, len(dokumen_sumber), 5000):
    batch = dokumen_sumber[i : i + 5000]
    collection.upsert(
        ids=[buat_id(d) for d in batch],
        documents=batch,
        metadatas=[{"source": "ingestion"} for _ in batch],
    )

Loop di atas memanggil collection.upsert(...) per 5000 dokumen. Ini menyeimbangkan throughput dengan penggunaan memori.

Tuning Index untuk Performa

Menggabungkan Parameter dari Episode 9

Untuk dataset besar, mulai dari pengaturan yang terbukti:

PythonKonfigurasi index untuk skala
collection = client.create_collection(
    name="skala-besar",
    metadata={
        "hnsw:space": "cosine",
        "hnsw:ef_construction": 200,
        "hnsw:M": 16,
    },
)

metadata={"hnsw:ef_construction": 200, "hnsw:M": 16} adalah titik awal sehat: build yang cukup akurat tanpa memboroskan memori berlebihan. Setelah data besar masuk, sesuaikan ef_search per query untuk menyeimbangkan recall dan latensi.

Menjaga Kualitas Index Saat Menulis

Setiap penambahan besar membuat index berubah. Untuk ingestion massal, pertimbangkan: masukkan dulu semua data dengan ef_construction standar, lalu — jika akurasi dirasa kurang — bangun ulang collection dengan nilai yang lebih tinggi. Menulis ulang (reindex) lebih murah daripada membangun ulang dari nol.

Optimasi Latensi Query

ef_search adalah knob paling berpengaruh pada latensi query. Nilai rendah mempercepat query tapi menurunkan recall; nilai tinggi sebaliknya. Mulai dengan 100 dan naikkan jika hasil dirasa kurang akurat:

PythonQuery dengan ef_search diatur
hasil = collection.query(
    query_texts=["topik pencarian"],
    n_results=10,
    search_params={"ef_search": 256},
)

search_params={"ef_search": 256} memakai kualitas pencarian lebih tinggi untuk query penting. Ukur dengan timing untuk menemukan titik optimal kasus kalian.

Pra-Filter Metadata: Memangkas Ruang Pencarian

Pra-filter metadata — memfilter sebelum pencarian vektor — memangkas jumlah vektor yang harus dibandingkan. Untuk collection yang besar, ini sering memberikan pengurangan latensi paling dramatis:

PythonPra-filter kategori
hasil = collection.query(
    query_texts=["deploy"],
    n_results=5,
    where={"category": "devops"},
    search_params={"ef_search": 200},
)

where={"category": "devops"} membatasi pencarian hanya pada vektor dengan kategori tersebut. Jika collection punya 5 juta vektor tapi hanya 200 ribu kategori devops, pencarian berjalan jauh lebih cepat tanpa mengorbankan akurasi.

Sizing Resource CPU dan RAM

Memperkirakan RAM yang Dibutuhkan

Estimasi kasar: index HNSW membutuhkan beberapa kali ukuran vektor mentah. Untuk vektor 384 dimensi (float32), satu juta vektor kira-kira 1,5 GB — plus overhead index sekitar 2-4 kali lipat.

Estimasi RAM untuk HNSW
1 juta vektor x 384 dimensi x 4 bytes ~= 1.5 GB
estimasi index total (overhead 2-4x) ~= 3-6 GB

Jadikan estimasi ini titik awal, lalu ukur penggunaan aktual dengan monitoring dari episode 20. RAM yang cukup menghindarkan swap yang menghancurkan latensi.

Menyusun Deployment Berdasarkan Beban

SkalaSaran ResourceStrategi
<100 ribu vektor2 CPU / 4 GBSingle instance
100 ribu - 5 juta4 CPU / 16 GBSingle instance + tuning
5 juta+8+ CPU / 32+ GBMulti-replica + load balancer

Untuk melewati 5 juta vektor, kombinasi episode 15 (load balancer) dan episode 19 (advanced query patterns) menjadi penting. Jangan lupa: setiap replica membutuhkan RAM untuk index-nya sendiri.

Penutup

Episode 17 membuat ChromaDB siap tumbuh: memahami fondasi server Rust untuk skala jutaan vektor, ingestion batch yang 10-100 kali lebih cepat, tuning index dari episode 9, optimasi ef_search dan pra-filter metadata untuk latensi, serta sizing CPU dan RAM berdasarkan volume data.

Inti yang harus dibawa pulang:

  • Server Rust adalah fondasi scaling; index HNSW hidup di RAM.
  • Ingestion batch 1.000-10.000 item jauh lebih cepat dari per-item.
  • Mulai dari ef_construction: 200 dan M: 16 untuk skala.
  • ef_search diatur per query; mulai 100, naikkan bila perlu.
  • Pra-filter metadata memangkas ruang pencarian secara dramatis.
  • Estimasi RAM: 3-6 GB per juta vektor 384 dimensi, lalu ukur.

Di episode 18 selanjutnya kita akan membahas integrasi dengan framework LLM — menjadikan ChromaDB sebagai VectorStore dan retriever di LangChain, adapter LlamaIndex dan framework lain, serta pola RAG production: chunking, embed, store, dan query dalam satu pipeline utuh. Semua kemampuan yang sudah kalian bangun sekarang bersatu.