Document MongoDB tidak statis: setiap $push menambah elemen array, setiap $set menambah field baru, dan document itu membesar perlahan sampai suatu hari menabrak dinding. Dindingnya bernama 16 MB — batas keras ukuran satu document BSON — dan jauh sebelum dinding itu tercapai, pertumbuhan sudah menagih biaya performa. Chapter ini mengukur pertumbuhan secara nyata, membuktikan batas 16 MB, lalu menerapkan pola untuk menjaga document tetap ramping.
Batas Keras 16 MB #
Satu document BSON tidak boleh melebihi 16.777.216 byte — angka yang tetap sama sejak dulu dan berlaku untuk semua storage engine. Kita buktikan dengan document yang sengaja dibuat sedikit lebih besar:
use latihan_modeling
db.dropDatabase()
db.uji.insertOne({ nama: "penanda" })
db.uji.insertOne({ isiBesar: "x".repeat(16 * 1024 * 1024 + 100) })Output yang Dihasilkan:
switched to db latihan_modeling
{ ok: 1, dropped: 'latihan_modeling' }
{
acknowledged: true,
insertedId: ObjectId('6ad399cb6b7cff408d93d855')
}
Uncaught
MongoServerError: object to insert too large. size in bytes: 16777353, max size: 16777216Server menolak dengan pesan yang jujur: object to insert too large, lengkap dengan ukuran document (16.777.353 byte) dan batasnya (16.777.216 byte). Perhatikan bahwa ukuran yang dihitung adalah ukuran BSON setelah diserialisasi — string, nama field, dan overhead ikut terhitung. Document penanda sebelumnya lolos karena hanya puluhan byte.
Biaya Pertumbuhan pada WiredTiger #
WiredTiger — storage engine bawaan sejak MongoDB 3.2 dan satu-satunya pilihan di versi modern — menyimpan document dalam bentuk terkompresi (block compression bawaan: snappy) dan mengelola versi document lewat MVCC. Ketika sebuah update membuat document lebih besar dari ruang yang tersedia di posisinya, WiredTiger menulis ulang document tersebut ke lokasi baru — perpindahan yang juga menuntut pembaruan entry index yang menunjuk ke posisi lama.
Dampak praktisnya bertumpuk untuk document yang tumbuh terus:
- Tulis makin mahal — setiap
$pushpada array panjang menulis ulang bagian document yang semakin besar, terkompresi atau tidak. - Working set membengkak — document gemuk memenuhi cache dengan data yang sering tidak dibaca (ulasan lama yang tak pernah diminta halamannya).
- Index ikut bekerja — perpindahan document menuntut pembaruan pointer di setiap index yang mengena.
Aturan praktisnya: pertumbuhan yang terbatas dan bisa diprediksi wajar saja; pertumbuhan yang tak terbatas (ulasan, komentar, log aktivitas) harus dipindahkan ke collection terpisah.
Mengukur Pertumbuhan dengan bsonsize #
Fungsi bsonsize() di mongosh mengembalikan ukuran BSON sebuah document dalam byte — alat ukur yang tepat untuk melihat growth secara langsung:
db.buku.insertOne({
_id: 101, judul: "Bumi", harga: 89000,
ulasan: [
{ pembaca: "Budi", bintang: 4 },
{ pembaca: "Siti", bintang: 5 }
]
})
bsonsize(db.buku.findOne({ _id: 101 }))
db.buku.updateOne({ _id: 101 }, { $push: { ulasan: { pembaca: "Dewi", bintang: 3 } } })
bsonsize(db.buku.findOne({ _id: 101 }))Output yang Dihasilkan:
{ acknowledged: true, insertedId: 101 }
132
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
171Document tumbuh dari satu angka ke angka berikutnya setiap ulasan masuk — dua digit byte hari ini, tetapi grafiknya linear tanpa langit-langit. Seribu ulasan berarti document ratusan kilobyte yang ditulis ulang pada setiap ulasan baru; sejuta ulasan berarti menabrak batas 16 MB.
Membatasi Array dengan $slice #
Solusi pertama untuk array yang tumbuh: batasi jumlahnya langsung di operasi $push lewat modifier $slice. Kita simpan hanya lima ulasan terakhir per buku:
db.buku.updateOne(
{ _id: 101 },
{ $push: { ulasan: { $each: [ { pembaca: "Ani", bintang: 5 }, { pembaca: "Rudi", bintang: 2 }, { pembaca: "Tono", bintang: 4 } ], $slice: -5 } } }
)
db.buku.findOne({ _id: 101 })
bsonsize(db.buku.findOne({ _id: 101 }))Output yang Dihasilkan:
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
_id: 101,
judul: 'Bumi',
harga: 89000,
ulasan: [
{ pembaca: 'Siti', bintang: 5 },
{ pembaca: 'Dewi', bintang: 3 },
{ pembaca: 'Ani', bintang: 5 },
{ pembaca: 'Rudi', bintang: 2 },
{ pembaca: 'Tono', bintang: 4 }
]
}
248Bedah operasinya:
$each— membawa daftar ulasan baru yang mau dimasukkan; wajib hadir karena$slicehanya berlaku bersama$each.$slice: -5— setelah$eachdieksekusi, MongoDB memangkas array menjadi lima elemen terakhir; nilai negatif berarti “simpan dari belakang”.- Hasilnya — ulasan
Budiyang paling awal terdorong keluar; document berhenti tumbuh dan ukurannya stabil di sekitar nilai tadi.
Pangkasan ini sengaja membuang data — ulasan Budi tidak lagi tersimpan di mana pun. Kalau seluruh riwayat tetap harus bisa diakses, pindahkan data lama ke collection terpisah dan simpan hanya yang panas di induk: itulah Subset Pattern yang kita bahas tuntas di chapternya sendiri.
Rangkuman #
- Batas 16 MB per document BSON bersifat mutlak; server menolak insert dengan
object to insert too large. - Pada WiredTiger, document yang tumbuh menuntut penulisan ulang dan berpotensi perpindahan lokasi plus pembaruan index — biaya yang naik seiring ukuran.
bsonsize()mengukur ukuran BSON document dan membuat pertumbuhan terlihat secara konkret.- Batasi array dengan
$push+$each+$slice, atau pindahkan data tak terbatas ke collection terpisah lewat Subset Pattern.
Pembatasan array hanyalah satu dari banyak pola siap pakai. Chapter berikutnya memetakan sembilan pola desain resmi MongoDB yang menjadi kosa kata bersama para perancang skema.