All Products
Search
Document Center

ApsaraDB for MongoDB:Jumlah\ database\ dan\ tabel\ yang\ berlebihan\ menyebabkan\ instans\ tersendat\ atau\ berperilaku\ tidak\ normal

Last Updated:Mar 29, 2026

Saat sebuah instans ApsaraDB for MongoDB berisi sejumlah besar koleksi, performanya menurun dan terjadi pengecualian. Mesin penyimpanan WiredTiger membuat file disk terpisah untuk setiap koleksi dan setiap indeks, serta setiap resource yang dibuka menggunakan handle data (dhandle) terkait di memori. Dengan ribuan koleksi, volume dhandle yang terbuka menyebabkan kontensi lock yang memperlambat semua operasi database.

Jumlah koleksi yang besar tidak selalu menimbulkan masalah. Dampaknya bergantung pada model bisnis dan workload Anda. Misalnya, dua instans dengan spesifikasi yang sama, 10.000 koleksi, dan 100.000 dokumen dapat berperilaku sangat berbeda:
Perangkat lunak akuntansi: Sebagian besar koleksi menyimpan data dingin dan jarang diakses. Dampak terhadap performa minimal.
Sistem multi-tenant: Koleksi diisolasi per tenant dan sebagian besar aktif digunakan. Kontensi lock sangat parah.

Cara kerjanya

Mesin penyimpanan WiredTiger membuat file disk terpisah untuk setiap koleksi dan setiap indeks. Setiap resource yang dibuka menggunakan struktur data unik bernama dhandle, yang melacak informasi checkpoint, referensi sesi, pointer ke struktur Pohon-B di memori, serta statistik data.

Saat jumlah koleksi meningkat, lebih banyak file sistem operasi dibuka dan lebih banyak dhandle terakumulasi di memori. Jumlah dhandle yang besar di memori menyebabkan kontensi lock, yang menurunkan performa instans.

Potensi masalah

  • Kueri lambat dan latensi tinggi akibat handle lock atau schema lock

  • Error kehabisan memori (OOM) selama inisialisasi sinkronisasi saat menambahkan node

  • Waktu restart instans yang lebih lama

  • Sinkronisasi data yang lebih lambat

  • Operasi backup dan restore yang lebih lambat

  • Tingkat kegagalan backup fisik yang lebih tinggi

  • Waktu pemulihan instans dari kegagalan yang lebih lama

Mendiagnosis kueri lambat

Saat kontensi lock akibat terlalu banyak koleksi menyebabkan kueri lambat, log kueri lambat berisi entri seperti berikut:

2024-03-07T15:59:16.856+0800 I  COMMAND  [conn4175155] command db.collections command: count { count: "xxxxxx", query: { A: 1, B: 1 },
$readPreference: { mode: "secondaryPreferred" }, $db: "db" } planSummary: COLLSCAN keysExamined:0 keysExaminedBySizeInBytes:0
docsExamined:1 docsExaminedBySizeInBytes:208 numYields:1 queryHash:916BD9E3 planCacheKey:916BD9E3 reslen:185
locks:{ ReplicationStateTransition: { acquireCount: { w: 2 } }, Global: { acquireCount: { r: 2 } }, Database: { acquireCount: { r: 2 } },
Collection: { acquireCount: { r: 2 } }, Mutex: { acquireCount: { r: 1 } } } storage:{ data: { bytesRead: 304, timeReadingMicros: 4 },
timeWaitingMicros: { handleLock: 40, schemaLock: 134101710 } } protocol:op_query 134268ms

Dalam contoh ini, operasi count pada koleksi berisi satu dokumen memakan waktu 134.268 ms. Bidang kuncinya adalah timeWaitingMicros: { handleLock: 40, schemaLock: 134101710 }, yang menunjukkan bahwa permintaan baca menghabiskan hampir seluruh waktunya menunggu untuk mendapatkan handle lock dan schema lock di lapisan penyimpanan—bukan melakukan pekerjaan aktual.

Tabel berikut menjelaskan bidang-bidang utama dalam entri log kueri lambat:

BidangDeskripsi
planSummaryRencana kueri yang digunakan. COLLSCAN menunjukkan pemindaian penuh koleksi tanpa indeks; IXSCAN menunjukkan pemindaian indeks.
keysExaminedJumlah kunci indeks yang dipindai.
docsExaminedJumlah dokumen yang dipindai. Nilai tinggi relatif terhadap jumlah hasil yang dikembalikan mengindikasikan indeks yang hilang atau suboptimal.
numYieldsJumlah kali operasi melepas lock-nya agar operasi lain dapat dilanjutkan.
timeWaitingMicros.handleLockWaktu yang dihabiskan menunggu untuk mendapatkan handle lock, dalam mikrodetik.
timeWaitingMicros.schemaLockWaktu yang dihabiskan menunggu untuk mendapatkan schema lock, dalam mikrodetik. Nilai tinggi merupakan indikator kuat adanya terlalu banyak dhandle yang terbuka.
protocol:op_query <N>msDurasi total operasi dalam milidetik.

Gunakan perintah berikut untuk mendiagnosis jumlah koleksi dan indeks sebelum mengambil tindakan:

// Menghitung jumlah koleksi dalam sebuah database
db.getSiblingDB(<dbName>).getCollectionNames().length

// Menampilkan statistik database (jumlah koleksi, jumlah indeks, jumlah dokumen, ukuran total)
db.getSiblingDB(<dbName>).stats()

// Menampilkan statistik untuk koleksi tertentu
db.getSiblingDB(<dbName>).<collectionName>.stats()

Metode optimasi

Pilih metode yang paling sesuai dengan situasi Anda. Mulailah dengan opsi yang paling tidak mengganggu terlebih dahulu.

Hapus koleksi yang tidak diperlukan

Identifikasi koleksi yang sudah kedaluwarsa atau tidak lagi diperlukan, lalu hapus menggunakan dropCollection. Untuk informasi lebih lanjut, lihat dropCollection().

Peringatan

Pastikan cadangan penuh tersedia sebelum menghapus koleksi apa pun.

Hapus indeks yang tidak diperlukan

Setiap indeks membuat file disk terpisah di WiredTiger dan menambahkan dhandle terkait. Mengurangi jumlah indeks secara langsung mengurangi tekanan dhandle.

Gunakan tahap agregasi $indexStats untuk mengidentifikasi indeks yang jarang digunakan. Jalankan perintah berikut sebelum melakukan perubahan (memerlukan izin yang sesuai):

// Menampilkan statistik akses untuk semua indeks dalam sebuah koleksi
db.getSiblingDB(<dbName>).<collectionName>.aggregate({"$indexStats":{}})

Contoh output:

{
   "name" : "item_1_quantity_1",
   "key" : { "item" : 1, "quantity" : 1 },
   "host" : "examplehost.local:27018",
   "accesses" : {
      "ops" : NumberLong(1),
      "since" : ISODate("2020-02-10T21:11:23.059Z")
   }
}

Tabel berikut menjelaskan bidang-bidang utama dalam output:

BidangDeskripsi
nameNama indeks
keyDetail kunci indeks
accesses.opsJumlah operasi yang menggunakan indeks (jumlah hit). Nilai ini direset saat instans direstart atau indeks dibangun ulang.
accesses.sinceTimestamp saat pengumpulan statistik dimulai

Terapkan aturan berikut saat memutuskan indeks mana yang harus dihapus:

  • Indeks tidak valid: Hapus indeks pada bidang yang tidak diakses oleh kueri apa pun.

  • Redundansi awalan indeks: Jika indeks {a:1} dan {a:1,b:1} keduanya ada, maka {a:1} bersifat redundan—indeks {a:1,b:1} mencakup semua kueri yang akan menggunakannya.

  • Urutan kueri ekuivalen: Jika indeks {a:1,b:1} dan {b:1,a:1} keduanya ada, hapus yang memiliki jumlah hit lebih sedikit. Untuk pencocokan ekuivalen, urutan bidang tidak memengaruhi hasil.

  • Aturan ESR untuk kueri rentang: Bangun indeks gabungan dalam urutan Equality, Sort, Range. Untuk informasi lebih lanjut, lihat Aturan ESR (Equality, Sort, Range).

  • Indeks dengan hit rendah: Indeks semacam ini sering kali diduplikasi oleh indeks dengan hit lebih tinggi. Evaluasi terhadap semua pola kueri sebelum menghapusnya.

Jika instans Anda menjalankan MongoDB 4.4 atau versi lebih baru, gunakan db.collection.hideIndex() untuk menyembunyikan indeks sebelum menghapusnya. Pantau instans selama periode tertentu untuk memastikan tidak ada kueri yang bergantung pada indeks tersebut sebelum menghapusnya secara permanen. Untuk informasi lebih lanjut, lihat db.collection.hideIndex().

Contoh: mengoptimalkan indeks dalam koleksi players

Koleksi players menyimpan data pemain dengan aturan bisnis berikut: setiap 20 coins secara otomatis dikonversi menjadi satu star.

// Struktur dokumen koleksi players
{
  "_id": "ObjectId(123)",
  "first_name": "John",
  "last_name": "Doe",
  "coins": 11,
  "stars": 2
}

Koleksi ini saat ini memiliki indeks berikut:

  • _id (default)

  • { last_name: 1 }

  • { last_name: 1, first_name: 1 }

  • { coins: -1 }

  • { stars: -1 }

Dengan menerapkan aturan di atas:

  • Hapus `{ coins: -1 }`: Tidak ada kueri yang mengakses bidang coins secara langsung.

  • Hapus `{ last_name: 1 }`: Indeks ini merupakan awalan dari { last_name: 1, first_name: 1 } sehingga bersifat redundan.

  • Pertahankan `{ stars: -1 }`: Meskipun $indexStats menunjukkan jumlah hit rendah, leaderboard akhir putaran memerlukan pengurutan pemain berdasarkan stars dalam urutan menurun. Menghapusnya akan memaksa pemindaian penuh koleksi.

Setelah optimasi, koleksi mempertahankan tiga indeks: _id, { last_name: 1, first_name: 1 }, dan { stars: -1 }. Hal ini mengurangi konsumsi penyimpanan dan meningkatkan performa penulisan.

Untuk pertanyaan lebih lanjut mengenai optimasi indeks, ajukan tiket.

Integrasikan data dari beberapa koleksi

Saat koleksi bertambah seiring waktu karena pola partisi berbasis waktu, menggabungkannya ke dalam satu koleksi menghilangkan masalah akumulasi tersebut.

Sebelum optimasi — database temperatures menyimpan pembacaan harian dalam koleksi terpisah:

// temperatures.march-09-2020
{ "_id": 1, "timestamp": "2020-03-09T010:00:00Z", "temperature": 29 }
{ "_id": 2, "timestamp": "2020-03-09T010:30:00Z", "temperature": 30 }
// ... total 25 pembacaan (sensor berjalan pukul 10:00–22:00, setiap 30 menit)
{ "_id": 25, "timestamp": "2020-03-09T022:00:00Z", "temperature": 26 }

// temperatures.march-10-2020
{ "_id": 1, "timestamp": "2020-03-10T010:00:00Z", "temperature": 30 }
// ...

Setiap hari baru membuat koleksi baru dan indeks _id baru. Kueri lintas hari memerlukan $lookup, yang performanya lebih buruk daripada kueri pada satu koleksi.

Setelah optimasi — satu koleksi temperatures.readings menyimpan semua data, satu dokumen per hari. Indeks default _id mendukung kueri berbasis tanggal tanpa perlu indeks tambahan untuk pembacaan setiap hari:

// temperatures.readings
{
  "_id": ISODate("2020-03-09"),
  "readings": [
    { "timestamp": "2020-03-09T010:00:00Z", "temperature": 29 },
    { "timestamp": "2020-03-09T010:30:00Z", "temperature": 30 },
    // ...
    { "timestamp": "2020-03-09T022:00:00Z", "temperature": 26 }
  ]
}
{
  "_id": ISODate("2020-03-10"),
  "readings": [
    { "timestamp": "2020-03-10T010:00:00Z", "temperature": 30 },
    { "timestamp": "2020-03-10T010:30:00Z", "temperature": 32 },
    // ...
    { "timestamp": "2020-03-10T022:00:00Z", "temperature": 28 }
  ]
}

Hal ini menghilangkan masalah pertumbuhan koleksi tak terbatas dan menghapus kebutuhan untuk membuat indeks setiap hari.

Untuk workload deret waktu, pertimbangkan penggunaan koleksi deret waktu (MongoDB 5.0 dan versi lebih baru). Untuk informasi lebih lanjut, lihat Deret Waktu.

Pisahkan instans

Jika jumlah total koleksi dalam instans mandiri ApsaraDB for MongoDB tidak dapat dikurangi, bagi data tersebut ke beberapa instans.

SkenarioSolusi pemisahanLangkah utama
Koleksi tersebar di beberapa databaseJika beberapa aplikasi atau layanan berbagi instans yang sama dan database mereka tidak saling terkait erat, migrasikan beberapa database ke instans ApsaraDB for MongoDB baru menggunakan Data Transmission Service (DTS). Untuk informasi lebih lanjut, lihat Migrasi data dari instans set replika ApsaraDB for MongoDB ke instans set replika atau kluster sharded ApsaraDB for MongoDB.Pilih source databases yang diperlukan saat membuat tugas DTS. Pertahankan atau ubah nama koleksi sesuai kebutuhan. Jalankan dropDatabase pada instans sumber setelah migrasi selesai.
Semua koleksi berada dalam satu databaseTentukan apakah koleksi dapat dipisahkan berdasarkan dimensi seperti wilayah, kota, atau prioritas. Gunakan DTS untuk memigrasikan subset koleksi ke instans ApsaraDB for MongoDB terpisah.Pilih source database yang diperlukan saat membuat tugas DTS. Jalankan perintah drop untuk menghapus koleksi yang telah dimigrasikan dari sumber. Kueri agregasi lintas instans memerlukan logika aplikasi tambahan.
Penting

Perbarui logika bisnis dan konfigurasi koneksi aplikasi Anda sebelum menyelesaikan migrasi.

Contoh: memisahkan platform multi-tenant

Platform manajemen multi-tenant menggunakan satu koleksi per tenant. Saat jumlah tenant melebihi 100.000, ukuran database mencapai terabyte dan kueri menjadi lambat.

Aplikasi membagi tenant berdasarkan wilayah: Tiongkok Utara, Tiongkok Timur Laut, Tiongkok Timur, Tiongkok Tengah, Tiongkok Selatan, Tiongkok Barat Daya, dan Tiongkok Barat Laut. DTS memigrasikan tenant ke instans ApsaraDB for MongoDB terpisah yang ditempatkan di wilayah yang sesuai. Data juga disinkronkan ke gudang data untuk agregasi dan analisis lintas wilayah.

Setelah pemisahan:

  • Setiap instans hanya menyimpan sebagian kecil dari jumlah koleksi awal, sehingga spesifikasi instans dapat dikurangi.

  • Permintaan dilayani oleh instans terdekat, mengurangi latensi hingga milidetik.

  • Kompleksitas operasi dan pemeliharaan per instans berkurang secara signifikan.

Migrasi ke kluster sharded menggunakan tag shard

Jika semua koleksi harus tetap berada dalam satu instans logis dan tidak dapat dikurangi, migrasikan ke instans kluster sharded dan gunakan tag shard untuk mengikat setiap koleksi ke shard tertentu. Hal ini mendistribusikan beban dhandle ke beberapa shard tanpa memerlukan perubahan aplikasi—hanya string koneksi yang berubah.

Sebagai contoh, 100.000 koleksi aktif yang dimigrasikan ke kluster 10-shard menghasilkan sekitar 10.000 koleksi per shard.

Untuk informasi lebih lanjut, lihat sh.addShardTag() dan sh.addTagRange().

Langkah-langkah

  1. Beli instans kluster sharded. Untuk informasi lebih lanjut, lihat Buat instans kluster sharded.

  2. Hubungkan ke node mongos dari instans kluster sharded. Untuk informasi lebih lanjut, lihat Hubungkan ke instans kluster sharded ApsaraDB for MongoDB menggunakan mongo shell.

  3. Tambahkan tag shard ke setiap shard:

    Akun harus memiliki izin yang diperlukan untuk menjalankan perintah ini. Data Management (DMS) tidak mendukung sh.addShardTag—gunakan mongo shell atau mongosh sebagai gantinya.
    sh.addShardTag("d-xxxxxxxxx1", "shard_tag1")
    sh.addShardTag("d-xxxxxxxxx2", "shard_tag2")
  4. Pra-konfigurasikan rentang tag untuk mengikat setiap koleksi ke satu shard. Gunakan [MinKey, MaxKey] untuk memastikan semua data dalam koleksi tetap berada di satu shard:

    use <dbName>
    sh.enableSharding("<dbName>")
    
    sh.addTagRange("<dbName>.test",  {"_id": MinKey}, {"_id": MaxKey}, "shard_tag1")
    sh.addTagRange("<dbName>.test1", {"_id": MinKey}, {"_id": MaxKey}, "shard_tag2")

    Ganti _id dengan kunci shard aktual Anda. Semua kueri harus menyertakan bidang kunci shard.

  5. Shard setiap koleksi:

    sh.shardCollection("<dbName>.test",  {"_id": 1})
    sh.shardCollection("<dbName>.test1", {"_id": 1})
  6. Jalankan sh.status() untuk memastikan aturan tag berlaku.

    image.png

  7. Migrasikan data ke instans kluster sharded. Untuk informasi lebih lanjut, lihat Migrasi data dari instans set replika ApsaraDB for MongoDB ke instans set replika atau kluster sharded ApsaraDB for MongoDB.

    Karena Anda telah pra-konfigurasi koleksi shard pada langkah 5, metadata koleksi sudah ada di target. Atur parameter Processing Mode of Conflicting Tables menjadi Ignore Errors and Proceed dalam tugas DTS.
  8. Setelah konsistensi data diverifikasi, alihkan aplikasi Anda ke instans kluster sharded.

Untuk menambahkan shard di kemudian hari, ulangi langkah 3 untuk menetapkan tag ke shard baru.
Jika koleksi baru dibuat setelah migrasi, ulangi langkah 4 dan 5 untuk setiap koleksi baru. Jika tidak, koleksi hanya akan ada di shard utama, yang menyebabkan jumlah koleksi di shard tersebut meningkat. Dalam kasus ini, instans Anda akan selalu dalam kondisi tersendat atau mengalami pengecualian.

Migrasi ke kluster sharded menggunakan zona

Zona bekerja sama seperti tag shard, tetapi menggunakan perintah yang lebih baru: sh.addShardToZone() dan sh.updateZoneKeyRange(). Untuk informasi lebih lanjut, lihat Kelola Zona Shard, sh.addShardToZone(), dan sh.updateZoneKeyRange().

Langkah-langkah

  1. Beli instans kluster sharded. Untuk informasi lebih lanjut, lihat Buat instans kluster sharded.

  2. Hubungkan ke node mongos dari instans kluster sharded. Untuk informasi lebih lanjut, lihat Hubungkan ke instans kluster sharded ApsaraDB for MongoDB menggunakan mongo shell.

  3. Tetapkan zona ke setiap shard:

    Akun harus memiliki izin yang diperlukan. Data Management (DMS) tidak mendukung sh.addShardToZone—gunakan mongo shell atau mongosh sebagai gantinya.
    sh.addShardToZone("d-xxxxxxxxx1", "ZoneA")
    sh.addShardToZone("d-xxxxxxxxx2", "ZoneB")
  4. Pra-konfigurasikan rentang kunci zona untuk mengikat setiap koleksi ke satu zona:

    use <dbName>
    sh.enableSharding("<dbName>")
    
    sh.updateZoneKeyRange("<dbName>.test",  { "_id": MinKey }, { "_id": MaxKey }, "ZoneA")
    sh.updateZoneKeyRange("<dbName>.test1", { "_id": MinKey }, { "_id": MaxKey }, "ZoneB")

    Ganti _id dengan kunci shard aktual Anda.

  5. Shard setiap koleksi:

    sh.shardCollection("<dbName>.test",  { _id: "hashed" })
    sh.shardCollection("<dbName>.test1", { _id: "hashed" })
  6. Jalankan sh.status() untuk memastikan aturan zona berlaku.

    image

  7. Migrasikan data ke instans kluster sharded. Untuk informasi lebih lanjut, lihat Migrasi data dari instans set replika ApsaraDB for MongoDB ke instans set replika atau kluster sharded ApsaraDB for MongoDB.

    Atur parameter Processing Mode of Conflicting Tables menjadi Ignore Errors and Proceed dalam tugas DTS.
  8. Setelah konsistensi data diverifikasi, alihkan aplikasi Anda ke instans kluster sharded.

Untuk menambahkan shard di kemudian hari, ulangi langkah 3 untuk menetapkan zona ke shard baru.
Jika koleksi baru dibuat setelah migrasi, ulangi langkah 4 dan 5 untuk setiap koleksi baru. Jika tidak, koleksi hanya akan ada di shard utama, yang menyebabkan jumlah koleksi di shard tersebut meningkat. Dalam kasus ini, instans Anda akan selalu dalam kondisi tersendat atau mengalami pengecualian.

Risiko

Hindari menjalankan dropDatabase pada database dengan banyak koleksi

Saat dropDatabase dijalankan, WiredTiger secara asinkron menghapus metadata dan file fisik semua koleksi dalam database tersebut. Hal ini dapat memengaruhi replikasi primary-to-secondary, menyebabkan lag replikasi terus meningkat. Mekanisme flow control MongoDB diaktifkan selama proses ini, dan semua operasi tulis dengan {writeConcern: majority} mungkin terpengaruh.

Untuk menghindari masalah ini, gunakan salah satu pendekatan berikut:

  • Hapus koleksi satu per satu dengan jeda antar penghapusan, lalu jalankan dropDatabase setelah semua koleksi dihapus.

  • Gunakan DTS atau alat migrasi lain untuk memigrasikan database dan koleksi yang ingin dipertahankan ke instans baru, lalu nonaktifkan instans asli setelah migrasi selesai.

Konfigurasikan peringatan lag replikasi pada instans Anda. Jika masalah ini terjadi, ajukan tiket untuk dukungan teknis.ajukan tiketajukan tiket

Ringkasan

  • Jumlah total koleksi dalam instans set replika tidak boleh melebihi 10.000. Jika jumlah total indeks dalam satu koleksi melebihi 15, kurangi jumlah tersebut.

  • Jika bisnis Anda memerlukan banyak koleksi—seperti isolasi multi-tenant berbasis koleksi—gunakan instans kluster sharded untuk mendistribusikan beban.

  • Jika database Anda memiliki jumlah koleksi yang besar dan Anda memerlukan bantuan, ajukan tiket.

Referensi