MongoDB çok fazla bellek kullanıyor


28

Birkaç haftadır MongoDB kullanıyoruz, gördüğümüz genel eğilim mongodb'un çok fazla bellek kullanmasıydı (veri kümesi + dizinlerinin tüm boyutundan çok daha fazla).

Bu soruyu ve bu soruyu çoktan okudum , ancak hiçbiri karşılaştığım konuyu ele almıyor gibi görünüyor, aslında belgelerde açıklanmış olanları açıklıyorlar.

Aşağıdakiler htop ve show dbs komutlarının sonuçlarıdır .

görüntü tanımını buraya girin

dbs göster

Mongodb'un IO eşlemeli bellek kullandığını biliyorum, bu yüzden temelde işletim sistemi bellekteki şeyleri önbelleğe alıyor ve başka bir işlem boş bellek istediğinde mongodb teorik olarak önbelleğe alınmış belleğe gitmesine izin vermeli , fakat gördüklerimize göre, değil.

OOM, postgres, redis, vb. Gibi diğer önemli süreçleri öldürmeye başladı. (Görüldüğü gibi, bu sorunun üstesinden gelmek için RAM'i şimdi çalışan ancak oldukça pahalı olan 183 GB'a yükselttik. veri setinin büyüklüğünün yaklaşık 4 katı

Yani,

  1. Bu kadar bellek kullanımı gerçekten beklenen ve normal mi? (Belgelere göre, WiredTiger önbelleği için en fazla ~% 60 RAM kullanıyor, ancak veri kümesi boyutunu göz önüne alarak 86GB RAM alabilmek için yeterli veriye sahip mi?)
  2. Bellek kullanımı bekleniyor olsa bile, başka bir işlem daha fazla bellek talep etmeye başlarsa, neden mongo ayrılan belleğini bırakmıyor? RAM'ı artırmadan ve sistemi tamamen dengesiz hale getirmeden önce, diğer çeşitli çalışan işlemler, mongodb'un kendisi de dahil olmak üzere linux oom tarafından sürekli olarak öldürülüyordu.

Teşekkürler !


4
Belki gibi WiredTiger dahili özelliklerine, üzerine sunumların bazı mongodb.com/presentations/... , bazı hafif tutabilir. Fiziksel RAM'in% 50'sinin varsayılan kullanımının, özel bir MongoDB sunucusunda muhtemelen neyin gerekli olduğuna dair bir tahmin olduğunu ve birçoğunun değiştirmesi gerekeceğini tahmin ediyorum. FWIW, cacheSizeGB ayarını mongoyu "sınırlıyor" olduğuna inanmıyorum - seçenek orada dağıtımlar üzerinde kontrol sahibi olmanızdır. Önbellek için ne kadar bellek mongo "ihtiyacı" belirlemek, sunucu önbellek istatistiklerini beklenen sunucu yükü altında izlemenizi gerektirir.

Yanıtlar:


23

Tamam, bu yüzden loicmathieu ve jstell tarafından verilen ipuçlarını izledikten ve biraz kazıdıktan sonra, bunlar, WiredTiger depolama motorunu kullanarak MongoDB hakkında öğrendiğim şeyler. Birisi aynı sorularla karşılaştıysa buraya koyuyorum.

Bahsettiğim bellek kullanım konuları, hepsi önceden tarihi olan WiredTiger'a ait 2012-2014'e aitti ve orijinal MMAPV1 depolama motorunun, ayrı bir önbelleği veya sıkıştırma desteği olmayan davranışını açıklıyor.

WiredTiger önbellek ayarları yalnızca doğrudan WiredTiger depolama motoru tarafından kullanılan belleğin boyutunu kontrol eder (mongod tarafından kullanılan toplam belleği değil). Diğer pek çok şey potansiyel olarak aşağıdakiler gibi bir MongoDB / WiredTiger yapılandırmasında bellek alıyor:

  • WiredTiger disk depolama alanını sıkıştırır, ancak bellekteki veriler sıkıştırılmaz.

  • WiredTiger, varsayılan olarak her bir işlemdeki verileri fesync yapmaz , bu nedenle günlük dosyaları da RAM’dedir ve bu da bellekte ücret alır. Ayrıca, G / Ç'yi verimli bir şekilde kullanmak için, WiredTiger’in G / Ç isteklerini (önbellek özlüyor) bir araya getirdiğini, bunun da bazı RAM aldıklarını (aslında kirli sayfaların (değiştirilmiş / güncellenmiş sayfaların) bir güncelleme listesine sahip olduğunu belirtmişlerdir. Onları bir Eşzamanlı SkipList'te saklar )

  • WiredTiger, önbelleğinde birden fazla kayıt sürümü tutuyor (Çoklu Sürüm Eşzamanlılık Kontrolü, işlemleri okumadan önceki taahhüt edilen işleme erişme işlemlerini okuyun).

  • WiredTiger Önbellekteki verilerin sağlama tutarlarını korur.

  • MongoDB'nin kendisi açık bağlantıları, toplamaları, sunucu kodunu vb . İşlemek için bellek kullanır .

Bu gerçekleri göz önünde bulundurarak, güvenmek show dbs;teknik olarak doğru değildi, çünkü yalnızca veri kümelerinin sıkıştırılmış boyutunu gösteriyor.

Veri setinin tam boyutunu elde etmek için aşağıdaki komutlar kullanılabilir.

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

Bu sonuç aşağıdaki gibidir:

{
    "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
}

Böylece gerçek veri kümesi büyüklüğü + dizinleri yaklaşık 68GB'lık bellek alıyor gibi görünüyor.

Tüm bunlar göz önüne alındığında, bellek kullanımının artık beklendiği tahmin ediliyor, iyi bir bölüm, G / Ç işlemlerini oldukça verimli bir şekilde ele aldığından (yukarıda açıklandığı gibi) WiredTiger önbellek boyutunu sınırlamanın tamamen iyi olması.

Ayrıca, bu sorunun üstesinden gelmek için OOM sorunu da var, mongodb'i almak için yeterli kaynağımız olmadığı için, OOM'un şu an için önemli süreçleri öldürmesini engellemek için oom_score_adj'i düşürdük (Anlamı , OOM'a bizim öldürmememizi söyledik. istenen işlemler ).


Benzer bir sorunumuz var. MongoDB RAM yemeye devam ediyor. Benzer oranlar. Çözüme ulaştığınız en oom_score_adj iyi şey çözüm miydi ?
Hartator

@Hartator Kablolu önleyicinin önbelleğini azalttık, dizinlerimizi ve dizin oluşturma politikamızı yönetme konusunda daha fazla çaba harcadık ve sonra nihayet umursadığımız şeyler için oom_score_adj'yi azalttık, sanırım bu zaten yapılabilecek her şey.
SpiXel

4

Burada, MongoDB ile ilgili bir sorunun olduğunu sanmıyorum, jstell size WiredTiger ile birlikte MongoDB'nin% 50 oranında kullanılabilir bellek kullanacağını, bu nedenle sunucunuzun RAM'ini yükseltirseniz daha fazla bellek alacağını söyledi.

DB + dizinlerinin boyutundan daha fazla olmasının nedeni olarak, WiredTiger'ın veritabanını diskte sıkıştırdığını ve ayrıca belge değişikliklerini kaydetmek için anlık görüntü günlükleri kullandığını unutmayın. Böylece WiredTiger'ın gerçek boyutu, show dbs * sıkıştırma_türümü + anlık görüntü günlüklerinin boyutunu kullanan boyuttur. Bu yüzden beklenen büyüklüğü bilmek neredeyse imkansız.

Araçları gibi akılda da tut top, ps, htopgerçekten uygulama tarafından kullanılan bellek görüntü vermedi, detaylar için bu SOW soruya refere: https://stackoverflow.com/questions/131303/how-to-measure-actual-memory -kullanım-of-an-uygulama ya da işlem

Şimdi sorununa geri dönelim. Aynı ana bilgisayarda çalışan başka araçlarınız var ve bir OOM onları öldürüyor. Linux OOM'a aşina değilim ama bunları MongoDB veya .. çünkü onlar yüzünden öldürdüğünden emin misiniz (belki Postgres'i çok fazla hafızaya aldığı için Postgres'i öldürür).

Her neyse, en iyi uygulama olarak, büyük bir Mongo veritabanınız varsa, onu başka veritabanlarıyla paylaşılan bir ana bilgisayara kurmayın ya da burada tanımladığınız gibi bir sorun olması durumunda çok fazla zorluk çekersiniz. kim gerçekten konuyla ilgili soruna neden oluyor.


4

Dokümanlar

MongoDB için temel bellek kaygılarını ve ayrıca bellek kullanımını kontrol etme konusundaki bu kısa tartışmayı okumak isteyebilirsiniz .

Hafıza kullanımına genel bakış

Komut db.serverStatus()( dokümanlar ) özellikle bellek kullanımına genel bir bakış sağlayabilir:

> 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 ...

Endekslerin ne kadar büyük?

db.stats() tüm dizinlerin toplam boyutunu gösterebilir, ancak tek bir koleksiyon için ayrıntılı bilgi alabiliriz. db.myCollection.stats()

Örneğin, bu komut her koleksiyon için dizinlerin boyutlarını karşılaştıracaktır :

> 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" }

Şimdi , hangi büyük endekslerden hangisinin en pahalı olduğunu görmek için bu büyük koleksiyonun ayrıntılarına bakabiliriz :

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

Bu bize tasarrufların nerede mümkün olabileceği konusunda daha iyi bir fikir verebilir.

(Bu durumda, üzerinde createTimeoldukça büyük bir indeks vardı - belge başına bir giriş - ve onsuz yaşayabileceğimize karar verdik.)


Dizinlerin bellek maliyeti yüksek mi?
Mathias Lykkegaard Lorenzen

@MathiasLykkegaardLorenzen Bu, sunucunuzun RAM'ına göre indekslediğiniz alan için benzersiz değerlerin sayısına bağlıdır. Bizim durumumuzda createTimeendeks sorunluydu çünkü her bir doküman için eşsizdi ve koleksiyon çok büyüktü. Diğer alanların indekslenmesi tamamdır, çünkü daha az benzersiz değer vardı (değerler kümelenmiştir).
joeytwiddle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.