MongoDB實例內(nèi)存使用率高問題
云數(shù)據(jù)庫 MongoDB 版的內(nèi)存使用率是一個非常重要的監(jiān)控指標(biāo)。本文介紹查看云數(shù)據(jù)庫 MongoDB 版實例內(nèi)存使用率的方法,以及導(dǎo)致內(nèi)存使用率高的原因和優(yōu)化策略。
背景信息
云數(shù)據(jù)庫 MongoDB 版進程啟動后,不僅會加載二進制文件和依賴的各種系統(tǒng)庫文件到內(nèi)存,而且負責(zé)內(nèi)存的分配和釋放工作,例如客戶端的連接管理、請求處理和存儲引擎等。默認情況下,云數(shù)據(jù)庫 MongoDB 版的內(nèi)存分配器是Google tcmalloc,內(nèi)存主要被Wiredtiger存儲引擎和客戶端連接及請求處理占用。
查看方法
分片集群架構(gòu)下,各個分片(Shard)的內(nèi)存使用與副本集架構(gòu)保持一致,Config Server用于存儲配置元數(shù)據(jù),Mongos路由節(jié)點的內(nèi)存使用率和聚合結(jié)果集、連接數(shù)大小、元數(shù)據(jù)大小相關(guān)。
副本集架構(gòu)下,您可以通過以下方法查看內(nèi)存的使用情況:
監(jiān)控圖分析
云數(shù)據(jù)庫 MongoDB 版副本集由多種角色組成,一個角色可能對應(yīng)一個或多個物理節(jié)點。云數(shù)據(jù)庫 MongoDB 版副本集實例提供一個可供讀寫訪問的Primary節(jié)點(主節(jié)點)、一個或多個提供高可用的Secondary節(jié)點(從節(jié)點)、一個隱藏的Hidden節(jié)點(隱藏節(jié)點)和一個或多個可選的ReadOnly節(jié)點(只讀節(jié)點)。
在MongoDB管理控制臺的監(jiān)控信息頁面,可以查看云數(shù)據(jù)庫 MongoDB 版的內(nèi)存使用率。
命令行查看
在MongoDB Shell中使用
db.serverStatus().mem
命令查看和分析內(nèi)存占用情況,返回示例如下:{ "bits" : 64, "resident" : 13116, "virtual" : 20706, "supported" : true } //resident 表示該mongod物理節(jié)點占用的物理內(nèi)存大小,單位為MB。 //virtual 表示該mongod物理節(jié)點占用的虛擬內(nèi)存大小,單位為MB。
說明serverStatus的更多信息,請參見serverStatus。
常見原因
引擎內(nèi)存
云數(shù)據(jù)庫 MongoDB 版的大部分內(nèi)存都會用于存儲引擎緩存。考慮到兼容性和安全性,云數(shù)據(jù)庫 MongoDB 版將存儲引擎WiredTiger的cachesize設(shè)置為實際申請的實例內(nèi)存規(guī)格大小的60%左右。具體規(guī)格,請參見產(chǎn)品規(guī)格。
如果存儲引擎緩存使用了cachesize配置大小的95%,說明實例負載已經(jīng)很高了。出于保護自身的目的,處理用戶請求的線程會主動參與到刷臟的工作中來,用戶側(cè)會明顯感覺到請求存在阻塞。具體規(guī)則,請參見 eviction參數(shù)說明。
您可以使用以下方法查看引擎內(nèi)存的使用情況:
在MongoDB Shell中通過
db.serverStatus().wiredTiger.cache
查看。返回信息中bytes currently in the cache
后的值為內(nèi)存大小。返回信息示例如下:{ ...... "bytes belonging to page images in the cache":6511653424, "bytes belonging to the cache overflow table in the cache":65289, "bytes currently in the cache":8563140208, "bytes dirty in the cache cumulative":NumberLong("369249096605399"), ...... }
在DAS控制臺的性能趨勢頁面實時查看當(dāng)前WiredTiger引擎的cache dirty比例。如何查看,請參見性能趨勢。
通過云數(shù)據(jù)庫 MongoDB 版自帶的mongostat工具查看當(dāng)前WiredTiger引擎的cache dirty比例。更多信息,請參見mongostat。
連接和請求占用的內(nèi)存
如果實例的連接數(shù)很大,可能會消耗?部分的內(nèi)存,原因如下:
每個連接,后端都有對應(yīng)處理這個連接上的請求的線程。每個線程最多可以開銷1MB的線程棧,通常情況下在幾十KB~幾百KB。
每個TCP連接在內(nèi)核層面有讀緩沖區(qū)和寫緩沖區(qū),由TCP內(nèi)核參數(shù)tcp_rmem和tcp_wmem等確定,這塊的內(nèi)存使用用戶無需關(guān)心。但并發(fā)連接越多,默認套接字緩存越大,則TCP占用內(nèi)存越大。
每接收到一個請求,會有個請求上下文,整個過程中可能分配很多臨時緩沖區(qū),比如請求包、應(yīng)答包和排序的臨時緩沖區(qū)等,這些在請求結(jié)束時都會釋放,但這個釋放只是歸還給內(nèi)存分配器 tcmalloc,tcmalloc優(yōu)先會還到自己的cache里,然后逐步再歸還給操作系統(tǒng)。
很多情況下,內(nèi)存使用率高的原因是tcmalloc未及時歸還內(nèi)存?操作系統(tǒng),這?塊最大可能達到幾十GB。關(guān)于tcmalloc未歸還OS的內(nèi)存大小,可以通過命令
db.serverStatus().tcmalloc
查看。其中tcmalloc cache=pageheap_free_bytes+total_free_byte。返回信息示例如下:{ "generic":{ "current_allocated_bytes":NumberLong("9641570544"), "heap_size":NumberLong("19458379776") }, "tcmalloc":{ "pageheap_free_bytes":NumberLong("3048677376"), "pageheap_unmapped_bytes":NumberLong("544994184"), "current_total_thread_cache_bytes":95717224, "total_free_byte":NumberLong(1318185960), ...... } }
說明mongodb tcmalloc的更多信息,請參見tcmalloc。
元數(shù)據(jù)信息占用的內(nèi)存
云數(shù)據(jù)庫 MongoDB 版的數(shù)據(jù)庫、集合、索引等內(nèi)存元數(shù)據(jù)等,如果集合和索引數(shù)量很多,這?塊占用的內(nèi)存也不容忽視。尤其在云數(shù)據(jù)庫 MongoDB 版4.0以前的版本,全量邏輯備份期間可能打開非常多的?件句柄并且未能及時歸還OS導(dǎo)致內(nèi)存快速上漲,或者低版本的云數(shù)據(jù)庫 MongoDB 版在大量刪除集合后可能未能刪除文件句柄導(dǎo)致內(nèi)存泄漏。
創(chuàng)建索引過程中的內(nèi)存消耗
正常的業(yè)務(wù)數(shù)據(jù)寫?情況下,Secondary節(jié)點會維持?個最大約256M的buffer用于數(shù)據(jù)回放。在創(chuàng)建索引方面,當(dāng)Primary節(jié)點創(chuàng)建索引完成后,Secondary節(jié)點回放過程中可能消耗更多的內(nèi)存。在云數(shù)據(jù)庫 MongoDB 版4.2以前,在Primary節(jié)點上通過非background的?式創(chuàng)建索引,后端回放創(chuàng)建索引是串行的,最多可能消耗500M內(nèi)存,而云數(shù)據(jù)庫 MongoDB 版4.2以后默認廢棄了background選項,允許Secondary節(jié)點并行回放創(chuàng)建索引,那就會消耗更多的內(nèi)存,多個索引同時創(chuàng)建時可能導(dǎo)致實例內(nèi)存溢出。
創(chuàng)建索引期間可能造成的內(nèi)存消耗,詳情請參見index-build-impact-on-database-performance和index-build-process。
PlanCache內(nèi)存占用
在某些場景下,一個請求可能存在的執(zhí)行計劃非常多,這時plancache會消耗比較多的內(nèi)存。在高版本云數(shù)據(jù)庫 MongoDB 版中,您可以使用mgset-xxx:PRIMARY> db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes
命令查看PlanCache占用的內(nèi)存大小。更多信息,請參見Secondary node memory arise while balancer doing work。
解決策略
內(nèi)存優(yōu)化并不是盡可能地減少內(nèi)存使用,而是在保證系統(tǒng)性能正常的前提下,內(nèi)存足夠使用且穩(wěn)定,在機器資源和性能中達到一個最佳的折衷。云數(shù)據(jù)庫 MongoDB 版幫助用戶指定了CacheSize的大小,該值不支持修改。解決內(nèi)存使用的策略如下:
控制并發(fā)連接數(shù)。根據(jù)性能測試結(jié)果,數(shù)據(jù)庫中能夠創(chuàng)建100個長連接,默認MongoDB Driver可以和后端建立100個連接池。當(dāng)存在很多客戶端時,就需要降低每個客戶端的連接池大小,一般建議與整個數(shù)據(jù)庫建立的長連接控制在1000以內(nèi),連接太多會導(dǎo)致內(nèi)存和多線程上下文的開銷增加,影響請求處理延時。
降低單次請求的內(nèi)存開銷,例如通過創(chuàng)建索引減少集合的掃描、內(nèi)存排序等。
在連接數(shù)合適的情況下內(nèi)存占用持續(xù)增高,建議升級內(nèi)存配置,避免可能存在內(nèi)存溢出和大量清除緩存而導(dǎo)致系統(tǒng)性能急劇下滑。
加速tcmalloc釋放內(nèi)存。如果您的數(shù)據(jù)庫實例內(nèi)存使用率超過80%,可以通過控制臺的參數(shù)設(shè)置調(diào)整tcmalloc相關(guān)參數(shù)進行優(yōu)化。優(yōu)先開啟tcmallocAggressiveMemoryDecommit參數(shù),因為此參數(shù)經(jīng)過豐富的實踐驗證,對于解決內(nèi)存相關(guān)問題有顯著效果。如果調(diào)整此參數(shù)后未達到預(yù)期效果,再考慮漸進式調(diào)大tcmallocReleaseRate參數(shù)值,例如參數(shù)初始值為1,先調(diào)整至3,再調(diào)整至5。
重要建議在業(yè)務(wù)低峰期進行調(diào)整,因為tcmallocAggressiveMemoryDecommit和tcmallocReleaseRate參數(shù)調(diào)整可能會導(dǎo)致數(shù)據(jù)庫性能退化,如果調(diào)整后對業(yè)務(wù)產(chǎn)生了影響請及時回滾。
如果您在使用云數(shù)據(jù)庫 MongoDB 版過程中遇到更多可能存在內(nèi)存泄漏的場景,可以聯(lián)系阿里云技術(shù)?持處理。
參考
eviction參數(shù)說明
參數(shù) | 默認值 | 含義 |
eviction_target | 80 | 當(dāng)cache used超過eviction_target,后臺evict線程開始淘汰CLEAN PAGE。 |
eviction_trigger | 95 | 當(dāng)cache used超過eviction_trigger,用戶線程也開始淘汰CLEAN PAGE。 |
eviction_dirty_target | 5 | 當(dāng)cache dirty超過eviction_dirty_target,后臺evict線程開始淘汰DIRTY PAGE。 |
eviction_dirty_trigger | 20 | 當(dāng)cache dirty超過eviction_dirty_trigger,用戶線程也開始淘汰DIRTY PAGE。 |