Katalog toko buku kita bertambah setiap hari: dari ratusan judul menjadi ribuan, lalu jutaan. Query find({ harga: 120000 }) yang tadinya instan mulai memakan ratusan milidetik, dan aplikasi terasa semakin berat. Penyebabnya hampir selalu sama: tanpa index, MongoDB harus memeriksa setiap document dalam collection untuk menjawab satu pertanyaan. Chapter ini membuktikan hal itu dengan angka nyata dari explain(), lalu memperlihatkan betapa cepatnya query yang sama setelah index dibuat — ditutup pembahasan biaya yang ditimbulkan index agar kita memasangnya secara sadar, bukan asal pasang.

Peta materi Chapter ini:

  • Demo COLLSCAN (collection scan) versus IXSCAN (index scan) dengan explain().
  • Perbandingan pertumbuhan kerja: linear versus logaritmik.
  • Biaya index: ruang penyimpanan dan perlambat operasi tulis.
  • Kapan index membantu dan kapan tidak.

Tanpa Index: Collection Scan #

Bayangkan mencari satu buku di gudang tanpa daftar isi: satu-satunya cara adalah memeriksa setiap rak hingga buku itu ditemukan. MongoDB melakukan hal yang sama melalui COLLSCAN — memindai seluruh collection dari document pertama hingga terakhir, menguji filter pada tiap document.

Siapkan katalog simulasi berisi 1.000 document. Data ini cukup besar agar angka pemeriksaan terlihat jelas, tetapi tetap ringan dijalankan:

js
use latihan_index
db.katalog_besar.drop()

const dokumen = Array.from({ length: 1000 }, (_, i) => ({
  judul: "Judul Buku " + i,
  penulis: "Penulis " + (i % 50),
  harga: 50000 + (i % 90) * 1000,
  kategori: ["fiksi", "nonfiksi", "sastra", "puisi"][i % 4]
}))
const hasil = db.katalog_besar.insertMany(dokumen)
db.katalog_besar.countDocuments({})

Output yang Dihasilkan:

text
switched to db latihan_index
true

1000

Sekarang cari semua buku berharga tepat 120000, lalu minta laporan eksekusinya lewat explain("executionStats"):

js
const rencana = db.katalog_besar.find({ harga: 120000 }).explain("executionStats")
printjson({
  tahap: rencana.queryPlanner.winningPlan.stage,
  documentDiperiksa: rencana.executionStats.totalDocsExamined,
  hasilDikembalikan: rencana.executionStats.nReturned
})

Output yang Dihasilkan:

text
{
  tahap: 'COLLSCAN',
  documentDiperiksa: 1000,
  hasilDikembalikan: 11
}

Mari kita bedah angka-angka tersebut:

  1. tahap: 'COLLSCAN': query planner tidak menemukan struktur bantu apa pun, sehingga memilih satu-satunya rencana yang tersedia — memindai collection.
  2. documentDiperiksa: 1000: seluruh 1.000 document dibaca dan diuji filternya, meskipun hanya 11 yang cocok. Kerja ini tumbuh linear mengikuti jumlah document.
  3. hasilDikembalikan: 11: dari 1.000 pemeriksaan hanya 11 yang berguna — rasio efisiensi 1,1%. Sisanya 98,9% adalah kerja yang terbuang, dan rasio ini memburuk setiap kali katalog bertambah.

Dengan Index: Index Scan #

Index menyimpan nilai field secara terurut dalam struktur B-tree, lengkap dengan penunjuk ke document aslinya. Karena nilainya terurut, MongoDB dapat melompat langsung ke posisi 120000 — mirip membuka kamus langsung ke halaman huruf yang dituju.

Buat index pada field harga, lalu jalankan query yang persis sama:

js
db.katalog_besar.createIndex({ harga: 1 })

const rencana = db.katalog_besar.find({ harga: 120000 }).explain("executionStats")
printjson({
  tahap: rencana.queryPlanner.winningPlan.inputStage.stage,
  index: rencana.queryPlanner.winningPlan.inputStage.indexName,
  kunciDiperiksa: rencana.executionStats.totalKeysExamined,
  documentDiperiksa: rencana.executionStats.totalDocsExamined,
  hasilDikembalikan: rencana.executionStats.nReturned
})

Output yang Dihasilkan:

text
harga_1

{
  tahap: 'IXSCAN',
  index: 'harga_1',
  kunciDiperiksa: 11,
  documentDiperiksa: 11,
  hasilDikembalikan: 11
}

Perhatikan tiga perubahan penting:

  1. tahap: 'IXSCAN': pemindaian kini lewat index harga_1, bukan collection.
  2. kunciDiperiksa: 11: hanya 11 kunci index disentuh — tepat sebanyak hasil yang dibutuhkan. Tidak ada pemeriksaan sia-sia.
  3. documentDiperiksa: 11: 11 document asli pun diambil untuk melengkapi hasil. Pada query berprojection khusus, angka ini bahkan bisa mencapai 0 — dibahas di Chapter Covered Queries.

O(n) versus O(log n) #

Perbedaan 1.000 menjadi 11 terlihat besar, tetapi kekuatan sebenarnya muncul saat data membesar. COLLSCAN tumbuh linear — dua kali data berarti dua kali kerja. Pencarian B-tree tumbuh logaritmik — dua kali data hanya menambah satu langkah perbandingan.

Jumlah documentCOLLSCAN (langkah)B-tree (langkah, aproksimasi)
1.0001.000~10
1.000.0001.000.000~20
1.000.000.0001.000.000.000~30

Tabel inilah alasan query pada collection ratusan juta document tetap terasa instan ketika index tersedia: jumlah langkah hampir tidak bertambah meski data bertambah seribu kali lipat.

Biaya Index: Ruang dan Kecepatan Tulis #

Index bukan fitur gratis yang cukup diaktifkan di mana-mana. Setiap index adalah struktur data terpisah yang harus selalu konsisten dengan collection-nya, dan konsistensi itu dibayar di dua tempat.

Pertama, ruang penyimpanan. Periksa berapa byte yang dipakai index kita:

js
const statistik = db.katalog_besar.stats()
printjson({
  jumlahDocument: statistik.count,
  ukuranData: statistik.size,
  ukuranIndex: statistik.totalIndexSize,
  rincianIndex: statistik.indexSizes
})

Output yang Dihasilkan:

text
{
  jumlahDocument: 1000,
  ukuranData: 103690,
  ukuranIndex: 28672,
  rincianIndex: {
    _id_: 4096,
    harga_1: 24576
  }
}

Satu index pada satu field kecil sudah memakan 24 KB untuk 1.000 document — hampir seperempat ukuran data aslinya yang 101 KB. Angka ini membesar seiring nilai yang di-index dan banyaknya index per collection.

Kedua, kecepatan tulis. Setiap insertOne, updateOne yang mengubah field ter-index, dan deleteOne harus memperbarui semua index collection tersebut sebelum operasi dianggap selesai. Sepuluh index berarti sepuluh pembaruan struktur untuk satu penulisan document.

Keseimbangan yang Sehat #

SituasiIndex membantu?
Query filter/sort pada field yang sering dibacaYa — penurunan waktu query biasanya jauh melebihi overhead tulis
Field yang hampir tidak pernah masuk queryTidak — hanya menambah biaya tulis dan ruang
Collection kecil (puluhan document)Pengaruh kecil; COLLSCAN pada data kecil tetap cepat
Aplikasi padat tulis, jarang bacaMinimal; setiap index memperlambat setiap insert
Query mengembalikan sebagian kecil dataYa — inilah kasus terbaik untuk index

Rangkuman #

  • Tanpa index, setiap query menjalankan COLLSCAN: seluruh document diperiksa, kerja tumbuh linear terhadap ukuran collection.
  • Dengan index, query berjalan lewat IXSCAN pada B-tree: jumlah kunci yang diperiksa mendekati jumlah hasil, kerja tumbuh logaritmik.
  • explain("executionStats") membuktikan perbedaan itu lewat totalDocsExamined dan totalKeysExamined.
  • Index dibayar dengan ruang penyimpanan dan perlambat operasi tulis pada setiap insert, update, dan delete.
  • Pasang index berdasarkan pola query nyata, bukan spekulasi — dan pantau pemakaiannya secara berkala.