Belajar MongoDB - Query Optimization & explain() Analysis
Episode 13 of 21

Belajar MongoDB - Query Optimization & explain() Analysis

Menganalisis execution plan dengan explain(), membedakan COLLSCAN dari IXSCAN, membaca winning plan dan rejected plans, memahami metrik totalKeysExamined dan totalDocsExamined, serta mengaktifkan database profiler untuk menemukan slow queries di produksi.

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

Pendahuluan

Di episode 12 kalian memahami index secara teori. Tapi di dunia nyata, "aku pikir query ini pakai index" tidak pernah cukup — kalian butuh bukti. Bagaimana cara mengetahui query mana yang lambat, kenapa lambat, dan apakah index baru benar-benar berfungsi? Jawabannya ada di explain() dan database profiler.

Episode 13 mengajarkan kalian menjadi detektif performa. Roadmap-nya: pertama kita baca execution plan dengan explain("executionStats"), kedua kita bedah perbedaan COLLSCAN dan IXSCAN, ketiga kita pahami winning plan versus rejected plans, keempat kita ukur efisiensi dengan metrik kunci, dan kelima kita temukan slow queries di produksi memakai profiler. Mari mulai.

Menganalisis Query Execution Plan

explain("executionStats")

explain() menunjukkan bagaimana MongoDB mengeksekusi sebuah query — plan apa yang dipilih dan berapa banyak kerja yang dilakukan. Tambahkan opsi "executionStats" untuk melihat statistik eksekusi nyata:

Melihat execution plan sebuah query
db.users.find({ email: "siti@example.com" }).explain("executionStats")

Hasilnya berupa dokumen besar berisi detail plan. Bagian paling penting ada di executionStats. Mari bedah field kunci yang wajib kalian kenal:

  • nReturned — berapa dokumen yang dikembalikan.
  • totalKeysExamined — berapa entri index yang diperiksa.
  • totalDocsExamined — berapa dokumen yang benar-benar dibuka dari collection.
  • executionTimeMillis — total waktu eksekusi dalam milidetik.

Aturan emas interpretasi: totalDocsExamined harus mendekati nReturned. Jika MongoDB membuka 100.000 dokumen hanya untuk mengembalikan 10, berarti ia memindai dokumen yang tidak perlu — pertanda index kurang tepat.

COLLSCAN vs IXSCAN

Di dalam plan, kalian akan menemukan stage utama yang menceritakan seluruh cerita:

  • IXSCAN — index scan. MongoDB memakai index untuk menemukan dokumen. Ini yang kita inginkan.
  • COLLSCAN — collection scan. MongoDB membaca seluruh dokumen satu per satu. Ini musuh utama.
Potongan output explain (disederhanakan)
executionStats: {
  nReturned: 10,
  totalKeysExamined: 10,
  totalDocsExamined: 10,
  executionTimeMillis: 2,
  executionStages: {
    stage: "FETCH",
    inputStage: { stage: "IXSCAN", indexName: "email_1" }
  }
}

Pada contoh sehat di atas: totalKeysExamined: 10 sama dengan nReturned: 10, totalDocsExamined: 10, dan stage-nya IXSCAN pada index email_1. Query ini optimal — setiap dokumen yang diperiksa memang dikembalikan.

Sebaliknya, jika kalian melihat COLLSCAN dan totalDocsExamined jauh melebihi nReturned, query inilah tersangka slow query.

Winning Plan vs Rejected Plans

MongoDB tidak hanya memakai satu index per query. Untuk query dengan beberapa index potensial, optimizer menjalankan beberapa kandidat plan secara paralel, mengukur biayanya, lalu memilih yang terbaik:

  • winningPlan — plan yang menang dan dipakai untuk mengeksekusi query.
  • rejectedPlans — plan yang kalah dalam kompetisi optimizer.
Melihat winning plan dan rejected plans
db.orders.find(
  { userId: ObjectId("66aaaaaaaaaaaaaaaaaaaaaaaa"), status: "paid" }
).explain("allPlansExecution")

Menggunakan opsi "allPlansExecution" menampilkan statistik semua plan yang diuji. Bagian ini sangat berguna saat menguji index baru: kalian bisa melihat apakah index yang kalian buat menjadi winningPlan atau masih dikalahkan index lain. Jika index baru kalian selalu muncul di rejectedPlans, berarti ia tidak memberikan keuntungan — atau urutan field compound index-nya kurang tepat (ingat aturan ESR di episode 12).

Metrik dan Teknik Optimasi

Identifikasi Query dengan Filter Keras di Front

Query paling mahal adalah yang tidak bisa memakai index sama sekali. Ciri-cirinya:

  • Operator $where dan $regex tanpa anchor di awal pola.
  • Negasi seperti $ne, $nin, $not pada field yang seharusnya dipakai index.
  • Operasi pada field dengan tipe data campuran.

Cek Index Coverage dengan explain

Uji efektivitas index dengan menambahkannya lalu membandingkan executionStats sebelum dan sesudah:

Membuat index lalu bandingkan dengan explain
db.orders.createIndex({ userId: 1, status: 1 })
 
db.orders.find(
  { userId: ObjectId("66aaaaaaaaaaaaaaaaaaaaaaaa"), status: "paid" }
).explain("executionStats")

Jalankan explain sebelum dan sesudah membuat index. Sebelumnya kalian mungkin melihat COLLSCAN; sesudahnya IXSCAN dengan totalDocsExamined yang menurun drastis. Inilah cara membuktikan nilai sebuah index dengan data, bukan perasaan.

Mengidentifikasi Slow Queries

Mengaktifkan Database Profiler

Query yang lambat jarang datang membawa tanda. Untuk menemukannya secara sistematis, aktifkan database profiler yang mencatat operasi melebihi ambang waktu tertentu:

Aktifkan profiler untuk operasi lebih dari 100ms
db.setProfilingLevel(1, { slowms: 100 })

Level 1 berarti aktif dan mencatat operasi yang melebihi slowms. Level 0 mematikan, dan level 2 mencatat semua operasi (hanya untuk debugging sesaat, karena berat).

Menganalisis system.profile

Semua operasi yang terekam disimpan di collection system.profile dalam database yang sama. Kalian bisa query collection ini seperti biasa:

Mencari slow query terbaru
db.system.profile.find({ millis: { $gt: 100 } })
  .sort({ ts: -1 })
  .limit(10)

Setiap dokumen berisi query asli, waktu eksekusi, dan planSummary yang menunjukkan apakah memakai index. Dari sinilah kalian mendapatkan bukti tak terbantahkan: query mana yang memakan waktu, seberapa sering, dan index apa yang dipakainya. Kemudian optimasi dengan index baru atau penulisan ulang query, dan verifikasi dengan explain.

Warning

Profiler level 2 (mencatat semua operasi) menambah beban signifikan karena setiap query ditulis ke system.profile. Jangan mengaktifkannya di produksi secara permanen — gunakan level 1 dengan slowms yang masuk akal (100ms adalah titik awal umum), lalu matikan atau naikkan ambangnya setelah investigasi selesai.

Info

Gabungkan temuan profiler dengan db.currentOp() untuk melihat operasi yang sedang berjalan saat ini — termasuk yang sudah berjalan lama dan perlu di-kill. Untuk operasi macet, jalankan db.currentOp() lalu hentikan dengan db.killOp(<opid>). Ini adalah skill penyelamat saat produksi tersendat karena satu query raksasa.

Penutup

Pada episode 13 ini kalian telah menjadi detektif performa: membaca execution plan dengan explain("executionStats"), membedakan COLLSCAN yang buruk dari IXSCAN yang baik, memahami winningPlan versus rejectedPlans dalam kompetisi optimizer, mengukur efisiensi lewat metrik totalKeysExamined dan totalDocsExamined, serta mengaktifkan database profiler untuk menemukan slow queries di produksi lewat system.profile.

Inti yang harus dibawa pulang:

  • totalDocsExamined harus mendekati nReturned; jika tidak, ada index yang kurang tepat.
  • COLLSCAN = musuh; IXSCAN = yang kita inginkan.
  • rejectedPlans menunjukkan index yang kalah — gunakan untuk memvalidasi desain index.
  • Profiler level 1 dengan slowms menemukan query lambat di produksi.
  • Selalu verifikasi perbaikan dengan explain sebelum dan sesudah.

Di episode 14 berikutnya kita masuk ke salah satu fitur paling dinanti: Multi-Document ACID Transactions. Kalian akan memahami kapan transaksi diperlukan, menulis transaksi yang mengubah beberapa dokumen lintas collection secara atomik — misalnya transfer saldo antar akun — serta mempelajari batasan dan best practices-nya. Sampai jumpa di episode 14!

Belajar MongoDB - Query Optimization & explain() Analysis | Belajar MongoDB