MongoDB menggunakan terlalu banyak memori

28

Kami telah menggunakan MongoDB selama beberapa minggu sekarang, tren keseluruhan yang telah kami lihat adalah bahwa mongodb menggunakan terlalu banyak memori (lebih dari seluruh ukuran dataset + indeksnya).

Saya sudah membaca pertanyaan ini dan pertanyaan ini , tetapi sepertinya tidak ada yang membahas masalah yang saya hadapi, mereka sebenarnya menjelaskan apa yang sudah dijelaskan dalam dokumentasi.

Berikut ini adalah hasil dari perintah htop dan show dbs .

masukkan deskripsi gambar di sini

tampilkan dbs

Saya tahu bahwa mongodb menggunakan memori yang dipetakan IO, jadi pada dasarnya OS menangani caching di memori, dan mongodb secara teoritis harus melepaskan memori yang di-cache ketika proses lain meminta memori bebas , tetapi dari apa yang telah kita lihat, tidak.

OOM mulai membunuh proses penting lainnya seperti postgres, redis, dll. (Seperti yang dapat dilihat, untuk mengatasi masalah ini, kami telah meningkatkan RAM hingga 183GB yang sekarang berfungsi tetapi cukup mahal. Mongo menggunakan ~ 87GB ram, hampir 4X dari ukuran seluruh dataset)

Begitu,

  1. Apakah penggunaan memori sebanyak ini benar-benar diharapkan dan normal? (Sesuai dokumentasi, WiredTiger menggunakan paling banyak ~ 60% RAM untuk cache-nya, tetapi mengingat ukuran dataset, apakah itu bahkan memiliki cukup data untuk dapat mengambil 86GB RAM?)
  2. Bahkan jika penggunaan memori diharapkan, mengapa Anda tidak akan melepaskan memori yang dialokasikan jika proses lain mulai meminta lebih banyak memori? Berbagai proses yang berjalan lainnya terus-menerus dibunuh oleh linux oom, termasuk mongodb itu sendiri, sebelum kami meningkatkan RAM dan itu membuat sistem benar-benar tidak stabil.

Terima kasih!

SpiXel
sumber
4
Mungkin beberapa presentasi tentang internal WiredTiger, seperti mongodb.com/presentations/… , dapat memberi sedikit penjelasan. Saya berharap penggunaan default 50% dari RAM fisik hanya menebak apa yang mungkin diperlukan pada host MongoDB khusus, dan banyak yang perlu mengubahnya. FWIW, saya tidak percaya pengaturan cacheSizeGB adalah "membatasi" mongo - opsi ada di sana sehingga Anda memiliki kontrol atas penyebaran. Menentukan berapa banyak memori mongo "kebutuhan" untuk cache akan mengharuskan Anda untuk memantau statistik cache server di bawah beban server yang diharapkan.

Jawaban:

23

Oke, jadi setelah mengikuti petunjuk yang diberikan oleh loicmathieu dan jstell, dan menggali sedikit, ini adalah hal-hal yang saya temukan tentang MongoDB menggunakan mesin penyimpanan WiredTiger. Saya meletakkannya di sini jika ada yang mengalami pertanyaan yang sama.

Utas penggunaan memori yang saya sebutkan, semuanya milik 2012-2014, semua WiredTiger pra-tanggal dan menggambarkan perilaku mesin penyimpanan MMAPV1 asli yang tidak memiliki cache terpisah atau dukungan untuk kompresi.

Pengaturan cache WiredTiger hanya mengontrol ukuran memori yang langsung digunakan oleh mesin penyimpanan WiredTiger (bukan total memori yang digunakan oleh mongod). Banyak hal lain yang berpotensi mengambil memori dalam konfigurasi MongoDB / WiredTiger, seperti berikut ini:

  • WiredTiger memampatkan penyimpanan disk, tetapi data dalam memori tidak terkompresi.

  • WiredTiger secara default tidak mensinkronisasi data pada setiap komit , sehingga file log juga dalam RAM yang mengambil korban pada memori. Disebutkan juga bahwa untuk menggunakan I / O secara efisien, WiredTiger memotong permintaan I / O (cache misses) secara bersamaan, yang tampaknya juga menggunakan beberapa RAM (Bahkan halaman yang kotor (halaman yang telah berubah / diperbarui) memiliki daftar pembaruan pada mereka disimpan dalam SkipList Bersamaan ).

  • WiredTiger menyimpan beberapa versi catatan dalam cache-nya (Kontrol Konversi Multi Versi, baca operasi, akses versi terakhir sebelum operasi mereka)

  • WiredTiger Menyimpan checksum data dalam cache.

  • MongoDB sendiri mengkonsumsi memori untuk menangani koneksi terbuka, agregasi, kode serveride dan sebagainya .

Mempertimbangkan fakta-fakta ini, mengandalkannya show dbs;secara teknis tidak benar, karena hanya menunjukkan ukuran terkompresi dari dataset.

Perintah-perintah berikut dapat digunakan untuk mendapatkan ukuran dataset lengkap.

db.getSiblingDB('data_server').stats()
# OR
db.stats()

Hasil ini adalah sebagai berikut:

{
    "db" : "data_server",
    "collections" : 11,
    "objects" : 266565289,
    "avgObjSize" : 224.8413545621088,
    "dataSize" : 59934900658, # 60GBs
    "storageSize" : 22959984640,
    "numExtents" : 0,
    "indexes" : 41,
    "indexSize" : 7757348864, # 7.7GBs
    "ok" : 1
}

Jadi sepertinya ukuran dataset aktual + indeksnya mengambil sekitar 68GB memori itu.

Mempertimbangkan semua ini, saya kira penggunaan memori sekarang cukup diharapkan, bagian baiknya adalah benar-benar oke untuk membatasi ukuran cache WiredTiger, karena menangani operasi I / O dengan cukup efisien (seperti dijelaskan di atas).

Masih ada masalah OOM, untuk mengatasi masalah ini, karena kami tidak memiliki sumber daya yang cukup untuk mengeluarkan mongodb, kami menurunkan oom_score_adj untuk mencegah OOM dari membunuh proses penting untuk saat ini (Artinya kami memberitahu OOM untuk tidak membunuh kami proses yang diinginkan ).

SpiXel
sumber
Kami memiliki masalah serupa. MongoDB terus memakan RAM. Proporsi serupa. Apakah oom_score_adj solusinya adalah hal terbaik yang Anda bisa buat?
Hartator
@Hartator Yah kami mengurangi cacheSize wiredtiger, melakukan lebih banyak upaya dalam mengelola indeks dan kebijakan pengindeksan kami, dan akhirnya, mengurangi oom_score_adj untuk hal-hal yang kami rawat, itu saya kira semua itu bisa dilakukan dengan cara apa pun.
SpiXel
4

Saya tidak berpikir Anda memiliki masalah di sini dengan MongoDB, seperti jstell mengatakan kepada Anda MongoDB dengan WiredTiger akan menggunakan 50% dari memori yang tersedia jadi jika Anda meningkatkan RAM server Anda, itu akan membutuhkan lebih banyak memori.

Karena itu lebih dari ukuran indeks DB +, perlu diingat bahwa WiredTiger mengompresi database pada disk dan juga menggunakan snapshot log untuk merekam perubahan dokumen. Jadi ukuran sebenarnya dari WiredTiger adalah ukuran menggunakan show dbs * compression_ration + size of snapshot logs. Jadi hampir tidak mungkin untuk mengetahui ukuran yang diharapkan.

Perlu juga diingat bahwa alat-alat seperti top, ps, htoptidak menampilkan memori benar-benar digunakan oleh aplikasi, mengacu ke Swiss SOW untuk rincian: https://stackoverflow.com/questions/131303/how-to-measure-actual-memory -penggunaan-aplikasi-atau-proses

Sekarang, kembali ke masalah Anda. Anda memiliki alat lain yang berjalan di host yang sama dan OOM membunuh mereka. Saya tidak terbiasa dengan Linux OOM tetapi apakah Anda yakin itu membunuh mereka karena MongoDB atau .. hanya karena mereka (mungkin itu membunuh Postgres karena Postgres mengambil terlalu banyak memori).

Bagaimanapun, sebagai praktik terbaik jika Anda memiliki database Mongo yang besar, jangan instal di host yang dibagikan dengan database lain atau Anda akan memiliki banyak kesulitan, jika ada masalah seperti yang Anda jelaskan di sini, untuk mengetahui yang benar-benar menyebabkan masalah pada host.

loicmathieu
sumber
4

Documents

Anda mungkin ingin membaca masalah memori dasar untuk MongoDB dan juga diskusi singkat ini tentang memeriksa penggunaan memori .

Ikhtisar penggunaan memori

Perintah db.serverStatus()( docs ) dapat memberikan gambaran umum penggunaan memori, khususnya:

> db.serverStatus().mem
{ "bits" : 64, "resident" : 27, "virtual" : 397, "supported" : true }

> db.serverStatus().tcmalloc
... not easy to read! ...

> db.serverStatus().tcmalloc.tcmalloc.formattedString
------------------------------------------------
MALLOC:        3416192 (    3.3 MiB) Bytes in use by application
MALLOC: +      4788224 (    4.6 MiB) Bytes in page heap freelist
MALLOC: +       366816 (    0.3 MiB) Bytes in central cache freelist
...
... a bunch of stats in an easier to read format ...

Seberapa besar indeks Anda?

db.stats() dapat menampilkan ukuran total semua indeks, tetapi kami juga bisa mendapatkan info terperinci untuk satu koleksi menggunakan db.myCollection.stats()

Misalnya, perintah ini akan membandingkan ukuran indeks untuk setiap koleksi :

> db.getCollectionNames().map(name => ({totalIndexSize: db.getCollection(name).stats().totalIndexSize, name: name})).sort((a, b) => a.totalIndexSize - b.totalIndexSize).forEach(printjson)
...
{ "totalIndexSize" : 696320, "name" : "smallCollection" }
{ "totalIndexSize" : 135536640, "name" : "bigCollection" }
{ "totalIndexSize" : 382681088, "name" : "hugeCollection" }
{ "totalIndexSize" : 511901696, "name" : "massiveCollection" }

Sekarang kita bisa melihat detail untuk koleksi besar itu, untuk melihat mana dari indeksnya yang paling mahal:

> db.massiveCollection.stats().indexSizes
{
        "_id_" : 230862848,
        "groupId_1_userId_1" : 49971200,
        "createTime_1" : 180301824,
        "orderId_1" : 278528,
        "userId_1" : 50155520
}

Ini bisa memberi kita ide yang lebih baik tentang di mana penghematan mungkin terjadi.

(Dalam hal ini, kami memiliki indeks createTimeyang agak besar - satu entri per dokumen - dan kami memutuskan kami bisa hidup tanpanya.)

joeytwiddle
sumber
Apakah indeks memiliki biaya memori yang besar?
Mathias Lykkegaard Lorenzen
@MathiasLykkegaardLorenzen Tergantung pada jumlah nilai unik untuk bidang yang telah Anda indeks, relatif terhadap RAM server Anda. Dalam kasus kami, createTimeindeks itu bermasalah karena unik untuk setiap dokumen, dan koleksi itu sangat besar. Pengindeksan bidang lain ok, karena ada nilai unik yang lebih sedikit (nilai dikelompokkan).
joeytwiddle