往期分享
RDS MySQL
RDS MySQL 執行個體空間問題 RDS MySQL 記憶體使用問題 RDS MySQL 活躍線程數高問題 RDS MySQL 慢SQL問題 RDS MySQL 執行個體IO高問題 RDS MySQL 小版本更新最佳實踐RDS PostgreSQL
RDS PostgreSQL 執行個體IO高問題 RDS PostgreSQL 慢SQL問題 RDS PostgreSQL CPU高問題RDS SQL Server
RDS SQL Server 磁盤IO吞吐高問題 RDS SQL Server CPU高問題 RDS SQL Server 空間使用問題Redis
Redis 流控問題 Redis 記憶體高問題 Redis CPU高問題概述
阿裡雲資料庫MongoDB的記憶體使用率是一個非常重要的監控名額,然而MongoDB的記憶體使用率并非是簡單的越小越好,而突發的高記憶體使用率也需要引起使用者足夠的關注,因為它往往是業務側的變動引起,突發的記憶體飙升容易引起執行個體OOM。
MongoDB 程序啟動後,除了跟普通程序一樣,加載 binary、依賴的各種library 到記憶體,其作為一個DBMS,還需要負責用戶端連接配接管理、請求處理、資料庫中繼資料、存儲引擎等很多工作,這些工作都涉及記憶體的配置設定與釋放。預設情況下,MongoDB使用Google tcmalloc作為記憶體配置設定器,記憶體占用的大頭主要是"Wiredtiger存儲引擎"與 "用戶端連接配接及請求的處理"。
本文将由淺入深幫您檢視、分析和優化雲資料庫MongoDB的記憶體使用。
檢視記憶體使用
部署架構為副本集模式下,提供有多種檢視記憶體使用的方法,您可以根據自身需求,由淺入深了解MongoDB的記憶體使用情況。
部署架構為分片叢集模式下,各個Shard的記憶體使用與副本集保持一緻;Config Server僅僅存儲配置中繼資料,基本上不會造成記憶體瓶頸,一般可以忽略;Mongos路由節點的記憶體使用往往與聚合結果集,連接配接數大小,中繼資料大小有關。
監控圖分析
MongoDB副本集由多種角色組成,一個角色可能對應一個或多個實體節點。阿裡雲MongoDB對使用者暴露Primary和Secondary節點,另外還提供有隻讀執行個體的角色。可以通過點選"監控資訊",選擇對應的角色檢視MongoDB記憶體有關的監控情況,如下圖:

除了總體記憶體使用率外,MongoDB的WiredTiger引擎的記憶體使用也至關重要,下圖展示了WiredTiger引擎的記憶體使用情況,其中"maximum bytes configured"即目前配置的引擎cache的總大小,與配置檔案中的cacheSizeGB保持一緻;"bytes_read_into_cache"表示每秒從磁盤将資料加載到記憶體的大小 ,"bytes_written_from_cache"表示每秒從cache中重新整理髒頁到磁盤的大小,這兩個值越大,反應記憶體使用越吃緊,磁盤壓力相應也會越大。
指令行檢視
除了使用阿裡雲控制台提供的監控圖檢視以外,您也可以直接使用MongoDB Shell指令檢視和分析記憶體占用情況,如下示例:
PRIMARY> db.serverStatus().mem
{ "bits" : 64, "resident" : 13116, "virtual" : 20706, "supported" : true }
//resident表示該mongod實體節點占用的實體記憶體大小,機關為M
//virtual表示該mongod實體節點占用的虛拟記憶體大小,機關為M
另外,MongoDB serverStatus中自帶了大量的關于wiredTiger引擎和tcmalloc的記憶體使用使用詳情,可以通過的db.serverStatus().wiredTiger.cache和db.serverStatus().tcmalloc進行檢視,後文我們會重點展開講解。
更多serverStatus的資訊展示詳情建議參考:
https://docs.mongodb.com/manual/reference/command/serverStatus/記憶體使用詳細分析
引擎記憶體使用
MongoDB使用tcmalloc作為記憶體配置設定器,其中WiredTiger引擎占用的Cache是其中最大的一部分,引擎記憶體最大使用記憶體由配置參數cachesize決定。為了相容性能和安全性,阿裡雲資料庫MongoDB為cachesize設定的大小預設為申請記憶體的60%左右,由于存在向上取整等因素,詳細的cachesize大小設定可參照下表:
class_code | 規格大小 | 實際大小 | Wt cacheSizeGB |
dds.mongo.small | 1024 | 2048 | 1 |
dds.mongo.mid | 4096 | ||
dds.mongo.standard | 7168 | 2 | |
dds.mongo.large | 8192 | 12288 | 5 |
dds.mongo.xlarge | 16384 | 24576 | 10 |
dds.mongo.2xlarge | 32768 | 49152 | 20 |
dds.mongo.4xlarge | 65536 | 98304 | 40 |
dds.mongo.monopolize | 225280 | 96 | |
dds.mongo.2xmonopolize | 450560 | 264 | |
mongo.x8.medium | |||
mongo.x8.large | |||
mongo.x8.xlarge | |||
mongo.x8.2xlarge | 131072 | 77 | |
mongo.x8.4xlarge | 262144 | 154 | |
dds.sn4.8xlarge.3 | 64 |
通常情況下,即使資料量遠超過cachesize的大小,wiredTiger也不會将cachesize耗盡,如果超過了cachesize配置大小的95%,那表示系統性能已經處于較為危險的狀态。WiredTiger 在記憶體使用接近一定門檻值就會開始做淘汰,避免記憶體使用滿了阻塞使用者請求,這個過程稱為"eviction"。
參數 | 預設值 | 含義 |
eviction_target | 80 | 當 cache used 超過 ,背景evict線程開始淘汰 CLEAN PAGE |
eviction_trigger | 95 | ,使用者線程也開始淘汰 CLEAN PAGE |
eviction_dirty_target | 當 cache dirty 超過 ,背景evict線程開始淘汰 DIRTY PAGE | |
eviction_dirty_trigger | , 使用者線程也開始淘汰 DIRTY PAGE |
在這個規則下,一個正常運作的 MongoDB 執行個體,cache used 一般會在
0.8 * cacheSizeGB
及以下,偶爾超出問題不大;如果出現 used>=95% 或者 dirty>=20%,并一直持續,說明記憶體淘汰壓力很大,使用者的請求線程會阻塞參與page淘汰,請求延時就會增加,這時可以考慮"擴大記憶體"或者"擴大IOPS"。
檢視目前wiredTigerd引擎占用的記憶體大小
檢視目前wiredTiger引擎的cache dirty比例
您可以通過mongostat或者阿裡雲資料庫自治服務DAS實時檢視目前的cache dirty,目前阿裡雲資料庫MongoDB暫不支援cache dirty曆史情況檢視。
更多mongostat的使用方式可以參考:
https://docs.mongodb.com/v4.2/reference/program/mongostat/連接配接和請求占用的記憶體
如果執行個體的連接配接數很大,可能會消耗相當一部分的記憶體,這是因為:
-
- 每個連接配接,後端啟動一個線程處理這個連接配接上的請求,每個線程最多1MB的線程棧開銷,平時一般在幾十KB - 幾百KB 之間。
- 每個tcp連接配接在核心層面有read、write buffer,極端情況下可能漲到16MB,由tcp核心參數tcp_rmem和tcp_wmem等确定,這塊的記憶體使用使用者無需關心。但并發連接配接越多,預設套接字緩存越大,則tcp占用記憶體越大。
- 每接收到一個請求,會有個請求上下文,整個過程中可能配置設定很多臨時buffer,比如請求包、應答包、從引擎資料的buffer、排序的臨時buffer等,這些在請求結束都會釋放,但這個釋放隻是說歸還給記憶體配置設定器 tcmalloc,tcmalloc優先會還到自己的cache裡;然後逐漸再歸還給作業系統。是以很多情況下,記憶體使用率高的原因是tcmalloc未及時歸還記憶體至作業系統,這一塊最大可能達到數十GB。
關于tcmalloc未歸還OS的記憶體大小,可以通過以下指令檢視:
tcmalloc cache大小=pageheap_free_bytes + total_free_byte
關于更多mongodb tcmalloc的更多内容參考:
https://mongoing.com/archives/34751中繼資料資訊占用的記憶體
MongoDB的database、collection、index等記憶體中繼資料等,如果集合和index數量很多,這一塊占用的記憶體也不容忽視。尤其在MongoDB4.0以前的版本,全量邏輯備份期間可能打開非常多的檔案句柄并且未能及時歸還OS導緻記憶體快速上漲,或者低版本的MongoDB在大量删除collection後可能未能删除檔案句柄導緻記憶體洩漏。
阿裡雲MongoDB建議庫表數量控制在10W以内,并使用MongoDB4.0以上的核心版本,更多關于這塊的詳細分析參考:
https://jira.mongodb.org/browse/WT-4336建立index過程中的記憶體消耗
正常的業務資料寫入情況下,Secondary會維持一個最大約256M的buffer用于資料回放。在Create Index方面,當Primary建立index完成後,Secondary節點回放過程中可能消耗更多的記憶體。在MongoDB4.2以前,在Primary上通過非background的方式create index,後端回放建立index是串行的,最多可能消耗500M記憶體;而MongoDB4.2以後預設廢棄了background選項,允許Secondary并行回放create index,那就會消耗更多的記憶體,多個index同時build時可能導緻執行個體OOM。
更多關于create index期間可能造成的記憶體消耗參考:
https://docs.mongodb.com/manual/core/index-creation/#index-build-impact-on-database-performance https://docs.mongodb.com/manual/core/index-creation/#index-build-processPlanCache記憶體占用
在某些場景下,比如一個SQL可能存在的執行計劃非常多,這時plancache可能會消耗比較多的記憶體,在高版本MongoDB中可以通過以下指令檢視PlanCache占用的記憶體大小,預設大小未Byte。
mgset-xxx:PRIMARY> db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes
NumberLong(750695)
更多内容參考我們給官方提的bug連結:
https://jira.mongodb.org/browse/SERVER-48400記憶體使用的通用優化思路
首先需要強調一點,記憶體優化并非是為了盡可能減少記憶體使用,而是在保證系統性能完全沒問題的前提下,記憶體使用盡可能穩定和夠用,進而在機器資源和性能中達到一個最佳的折衷。
阿裡雲MongoDB幫使用者指定了比較合适的CacheSize大小,不建議也無法修改該值。
控制并發連接配接數,這個是最直接有效的方法,根據性能測試結果,100個長連接配接足以壓滿資料庫,預設 MongoDB driver也是跟後端建立100的連接配接池。當連接配接到用戶端(ECS機器)很多時,就需要降低每個用戶端的連接配接池大小,一般建議跟整個資料庫建立的長連接配接控制在1000以内,連接配接太多,一是記憶體開銷,另外多線程上下文的開銷也會增加,影響請求處理延時。
降低單次請求的記憶體開銷,比如通過建索引,減少 COLLSCAN、記憶體排序等。
另外,在連接配接數合适的情況下記憶體占用持續走高,建議更新記憶體配置,進而避免可能存在OOM和瘋狂的evict導緻系統性能急劇下滑。
最後,如果您在使用阿裡雲MongoDB過程中遇到更多可能存在記憶體洩漏的場景,可以與技術支援人員聯系。