Aplikasi berubah: fitur baru menghadirkan query baru, fitur lawas ditinggalkan, dan index yang dulu menyelamatkan performa kini hanya menambah biaya tulis tanpa pernah terpakai. Index bukan benda set-and-forget — ia perlu diinventarisasi, dipantau pemakaiannya, diubah tanpa membongkar aplikasi, dan sesekali dihapus. Chapter ini menutup Section Indexing dengan rutinitas perawatan index yang aman dijalankan di produksi.

Peta materi Chapter ini:

  • Inventaris index dengan getIndexes() dan ukurannya lewat stats().
  • Memantau pemakaian lewat $indexStats.
  • Mengubah properti index tanpa membangun ulang lewat collMod.
  • Menghapus, membangun ulang, dan disiplin jumlah index.

Menginventarisasi dan Menimbang Index #

Perawatan dimulai dari mengetahui apa yang ada. Siapkan katalog dengan tiga index — termasuk satu yang kita sengaja buat berlebihan:

js
use latihan_index
db.buku.drop()
db.buku.insertMany([
  { judul: "Laut Bercerita", penulis: "Leila S. Chudori", harga: 98000, kategori: "fiksi" },
  { judul: "Bumi", penulis: "Tere Liye", harga: 89000, kategori: "fiksi" },
  { judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 89000, kategori: "nonfiksi" },
  { judul: "Atomic Habits", penulis: "James Clear", harga: 115000, kategori: "nonfiksi" },
  { judul: "Rumah Kaca", penulis: "Tere Liye", harga: 72000, kategori: "fiksi" },
  { judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 105000, kategori: "sastra" }
])
db.buku.createIndex({ kategori: 1, harga: 1 })
db.buku.createIndex({ kategori: 1 })
db.buku.createIndex({ penulis: 1 })
printjson(db.buku.getIndexes().map(i => i.name))
const stat = db.buku.stats()
printjson({ jumlahIndex: stat.nindexes, totalUkuranIndexByte: stat.totalIndexSize })

Output yang Dihasilkan:

text
switched to db latihan_index
true
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('6ad3c00d09595cd04215bc84'),
    '1': ObjectId('6ad3c00d09595cd04215bc85'),
    '2': ObjectId('6ad3c00d09595cd04215bc86'),
    '3': ObjectId('6ad3c00d09595cd04215bc87'),
    '4': ObjectId('6ad3c00d09595cd04215bc88'),
    '5': ObjectId('6ad3c00d09595cd04215bc89')
  }
}
kategori_1_harga_1
kategori_1
penulis_1
[
  '_id_',
  'kategori_1_harga_1',
  'kategori_1',
  'penulis_1'
]

{
  jumlahIndex: 4,
  totalUkuranIndexByte: 65536
}

getIndexes() mendaftar empat index; stats() melengkapi dengan angka: nindexes menghitungnya dan totalIndexSize menjumlahkan ruang yang mereka pakai — informasi yang makin penting saat collection membesar. Perhatikan juga daftar namanya: kategori_1 adalah index berlebihan, karena setiap query berprefix kategori sudah terlayani oleh kategori_1_harga_1 (aturan prefix di Chapter Compound Index). Menyimpan keduanya berarti membayar dua kali biaya tulis untuk satu pekerjaan.

Memantau Pemakaian dengan $indexStats #

Teori redundansi bisa salah — bukti yang jujur adalah penghitung pemakaian. Stage agregasi $indexStats melaporkan berapa kali tiap index dipakai planner sejak index dibuat (atau server dinyalakan):

js
db.buku.find({ kategori: "fiksi", harga: { $lt: 90000 } }).toArray()
db.buku.find({ penulis: "Tere Liye" }).toArray()
printjson(db.buku.aggregate([
  { $indexStats: {} },
  { $project: { name: 1, "accesses.ops": 1 } }
]).toArray())

Output yang Dihasilkan:

text
[
  {
    _id: ObjectId('6ad3c00d09595cd04215bc88'),
    judul: 'Rumah Kaca',
    penulis: 'Tere Liye',
    harga: 72000,
    kategori: 'fiksi'
  },
  {
    _id: ObjectId('6ad3c00d09595cd04215bc85'),
    judul: 'Bumi',
    penulis: 'Tere Liye',
    harga: 89000,
    kategori: 'fiksi'
  }
]
[
  {
    _id: ObjectId('6ad3c00d09595cd04215bc85'),
    judul: 'Bumi',
    penulis: 'Tere Liye',
    harga: 89000,
    kategori: 'fiksi'
  },
  {
    _id: ObjectId('6ad3c00d09595cd04215bc88'),
    judul: 'Rumah Kaca',
    penulis: 'Tere Liye',
    harga: 72000,
    kategori: 'fiksi'
  }
]
[
  {
    name: 'penulis_1',
    accesses: {
      ops: Long('1')
    }
  },
  {
    name: 'kategori_1_harga_1',
    accesses: {
      ops: Long('1')
    }
  },
  {
    name: '_id_',
    accesses: {
      ops: Long('0')
    }
  },
  {
    name: 'kategori_1',
    accesses: {
      ops: Long('0')
    }
  }
]

Dua query yang baru dijalankan menyalakan dua penghitung: kategori_1_harga_1 dan penulis_1 masing-masing tercatat satu operasi, sementara kategori_1 dan _id_ tetap nol — sesuai dugaan redundansi kita. Field since (dihilangkan di projection) menandai kapan penghitung mulai berjalan, jadi bandingkan ops terhadap rentang waktunya. Di produksi, pasang pemantauan berkala: index dengan ops: 0 selama berminggu-minggu adalah kandidat penghapusan berikutnya.

Mengubah Index Tanpa Membangun Ulang #

Beberapa properti index bisa diubah setelah index hidup lewat collMod — tanpa drop dan tanpa membangun ulang. Dua kasus yang sudah kita uji di chapter sebelumnya:

PerubahanPerintahDibahas di
Umur TTLcollMod dengan keyPattern + expireAfterSeconds baruTTL Index
Sembunyikan/tampilkanhideIndex() / unhideIndex() (pembungkus collMod)Index Properties

Yang tidak bisa berubah adalah inti index: key pattern, tipe, dan opsi unique menuntut index dibangun ulang. Prosedurnya sederhana — dropIndex() lalu createIndex() — dan sejak MongoDB 4.2 pembangunan index memakai metode hibrida yang efisien: tahap awalnya membangun sambil collection tetap melayani baca-tulis, sehingga jendela gangguannya jauh lebih sempit daripada anggapan “index build mengunci database” dari era lawas.

Menghapus Index Berlebihan #

Eksekusi keputusan kita: hapus kategori_1 berdasarkan namanya, lalu timbang ulang collectionnya:

js
const hasil = db.buku.dropIndex("kategori_1")
printjson({ jumlahIndexSebelumnya: hasil.nIndexesWas, status: hasil.ok })
const stat = db.buku.stats()
printjson({ jumlahIndex: stat.nindexes, totalUkuranIndexByte: stat.totalIndexSize })

Output yang Dihasilkan:

text
{
  jumlahIndexSebelumnya: 4,
  status: 1
}

{
  jumlahIndex: 3,
  totalUkuranIndexByte: 45056
}

Respon nIndexesWas: 4 mencatat jumlah index sebelum penghapusan; inventaris kini tinggal tiga dan ruang index menyusut dari 65.536 menjadi 45.056 byte. Dua aturan penghapusan yang perlu dipegang: selalu hapus berdasarkan nama dari getIndexes() (bukan key pattern yang dikira-kira), dan jangan pernah menyentuh _id_ — MongoDB menolaknya dengan galat cannot drop _id index seperti yang kita lihat di Chapter Single Field Index.

Disiplin Jumlah Index #

Setiap index adalah kontrak dengan biaya tetap: setiap insert, update, dan delete harus menulis ke semua index collection. Sembarang menambah index justru memperlambat tulis, memakan RAM (index harus muat di memori untuk performa puncak), dan memperumit keputusan planner. Praktik yang mapan:

PraktikAlasan
Mulai dari query nyata, bukan tebakan fieldIndex tanpa query pemakai adalah beban mati
Prioritaskan compound sesuai pola ESRSatu compound sering menggantikan 2-3 index tunggal
Tinjau $indexStats secara berkalaBukti pemakaian lebih jujur daripada dugaan
Hapus redundan (prefix duplikat)Query berprefix sudah terlayani compound
Ukur dampak dengan explain() sebelum-sesudahKeputusan berbasis rencana eksekusi, bukan perasaan

Rangkuman #

  • getIndexes() menginventarisasi index; stats() menambahkan nindexes dan totalIndexSize untuk menimbang biaya ruang.
  • $indexStats menghitung pemakaian tiap index — dasar bukti untuk menghapus index yang tak terpakai, idealnya setelah masa percobaan hideIndex().
  • collMod mengubah TTL dan status hidden tanpa membangun ulang; key pattern dan unique menuntut drop + create.
  • Pembangunan ulang sejak 4.2 bersifat hibrida dan minim gangguan; hapus index selalu berdasarkan nama, dan _id_ tidak boleh disentuh.
  • Jaga jumlah index tetap kecil dan terukur: setiap index memperbesar biaya tulis dan pemakaian memori.