MongoDB記憶體使用分析和優化
MongoDB記憶體佔用點
Mongod 程式啟動後,除了跟普通程式一樣,載入 binary、依賴的各種library 到記憶體,其作為一個DBMS,還需要負責客戶端連線管理,請求處理,資料庫後設資料、儲存引擎等很多工作,這些工作都涉及記憶體的分配與釋放,預設情況下,MongoDB 使用 Google tcmalloc 作為記憶體分配器,記憶體佔用的主要是「儲存引擎」與 「客戶端連線及請求的處理」。
儲存引擎
MongoDB 3.2 及以後,預設使用 WiredTiger 儲存引擎,可通過 cacheSizeGB
選項配置 WiredTiger 引擎使用記憶體的上限,一般建議配置在系統可用記憶體的60%左右(預設配置)。
舉個例子,如果 cacheSizeGB
配置為 10GB,可以認為 WiredTiger 引擎通過tcmalloc分配的記憶體總量不會超過10GB。為了控制記憶體的使用,WiredTiger 在記憶體使用接近一定閾值就會開始做淘汰,避免記憶體使用滿了阻塞使用者請求。
目前有4個可配置的引數來支援 wiredtiger 儲存引擎的 eviction 策略(一般不需要修改),其含義是:
引數 | 預設值 | 含義 |
---|---|---|
eviction_target | 80 | 當 cache used 超過 eviction_target ,後臺evict執行緒開始淘汰 CLEAN PAGE |
eviction_trigger | 95 | 當 cache used 超過 eviction_trigger ,使用者執行緒也開始淘汰 CLEAN PAGE |
eviction_dirty_target | 5 | 當 cache dirty 超過 eviction_dirty_target ,後臺evict執行緒開始淘汰 DIRTY PAGE |
eviction_dirty_trigger | 20 | 當 cache dirty 超過 eviction_dirty_trigger , 使用者執行緒也開始淘汰 DIRTY PAGE |
在這個規則下,一個正常執行的 MongoDB 例項,cache used 一般會在 0.8 * cacheSizeGB
及以下,偶爾超出問題不大;如果出現 used>=95% 或者 dirty>=20%,並一直持續,說明記憶體淘汰壓力很大,使用者的請求執行緒會阻塞參與page淘汰,請求延時就會增加,這時可以考慮「擴大記憶體」或者 「換更快的磁碟提升IO能力」。
TCP 連線及請求處理
MongoDB Driver 會跟 mongod 程式建立 tcp 連線,並在連線上傳送資料庫請求,接受應答,tcp 協議棧除了為連線維護socket後設資料,每個連線會有一個read buffer及write buffer,使用者收發網路包,buffer的大小通過如下sysctl系統引數配置,分別是buffer的最小值、預設值以及最大值,詳細解讀可以google。
net.ipv4.tcp_wmem = 8192 65536 16777216
net.ipv4.tcp_rmem = 8192 87380 16777216
redhat7(redhat6上並沒有匯出這麼詳細的資訊) 上通過 ss -m
可以檢視每個連線的buffer的資訊,如下是一個示例,讀寫 buffer 分別佔了 2357478bytes、2626560bytes,即均在2MB左右;500個類似的連線就會佔用掉 1GB 的記憶體;buffer 佔到多大,取決於連線上傳送/應答的資料包的大小、網路質量等,如果請求應答包都很小,這個buffer也不會漲到很大;如果包比較大,這個buffer就更容易漲的很大。
tcp ESTAB 0 0 127.0.0.1:51601 127.0.0.1:personal-agent
skmem:(r0,rb2357478,t0,tb2626560,f0,w0,o0,bl0)
除了協議棧上的記憶體開銷,針對每個連線,Mongod 會起一個單獨的執行緒,專門負責處理這條連線上的請求,mongod 為處理連線請求的執行緒配置了最大1MB的執行緒棧,通常實際使用在幾十KB左右,通過 proc 檔案系統看到這些執行緒棧的實際開銷。 除了處理請求的執行緒,mongod 還有一系列的後臺執行緒,比如主備同步、定期重新整理 Journal、TTL、evict 等執行緒,預設每個執行緒最大ulimit -s
(一般10MB)的執行緒棧,由於這批執行緒數量比較固定,佔的記憶體也比較可控。
# cat /proc/$pid/smaps
7f563a6b2000-7f563b0b2000 rw-p 00000000 00:00 0
Size: 10240 kB
Rss: 12 kB
Pss: 12 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 12 kB
Referenced: 12 kB
Anonymous: 12 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
執行緒在處理請求時,需要分配臨時buffer儲存接受到的資料包,為請求建立上下文(OperationContext),儲存中間的處理結果(如排序、aggration等)以及最終的應答結果等。
當有大量請求併發時,可能會觀察到 mongod 使用記憶體上漲,等請求降下來後又慢慢釋放的行為,這個主要是 tcmalloc 記憶體管理策略導致的,tcmalloc 為效能考慮,每個執行緒會有自己的 local free page cache,還有 central free page cache;記憶體申請時,按 local thread free page cache ==> central free page cache 查詢可用記憶體,找不到可用記憶體時才會從堆上申請;當釋放記憶體時,也會歸還到 cache 裡,tcmalloc 後臺慢慢再歸還給 OS, 預設情況下,tcmalloc 最多會 cache min(1GB,1/8 * system_memory) 的記憶體, 通過 setParameter.tcmallocMaxTotalThreadCacheBytesParameter
引數可以配置這個值,不過一般不建議修改,儘量在訪問層面做調優)
tcmalloc cache的管理策略,MongoDB 層暴露了幾個引數來調整,一般不需要調整,如果能清楚的理解tcmalloc原理及引數含義,可做針對性的調優;MongoDB tcmalloc 的記憶體狀態可以通過 db.serverStatus().tcmalloc
檢視,具體含義可以看 tcmalloc 的文件。重點可以關注下 total_free_bytes
,這個值告訴你有多少記憶體是 tcmalloc 自己快取著,沒有歸還給 OS 的。
mymongo:PRIMARY> db.serverStatus().tcmalloc
{
"generic" : {
"current_allocated_bytes" : NumberLong("2545084352"),
"heap_size" : NumberLong("2687029248")
},
"tcmalloc" : {
"pageheap_free_bytes" : 34529280,
"pageheap_unmapped_bytes" : 21135360,
"max_total_thread_cache_bytes" : NumberLong(1073741824),
"current_total_thread_cache_bytes" : 1057800,
"total_free_bytes" : 86280256,
"central_cache_free_bytes" : 84363448,
"transfer_cache_free_bytes" : 859008,
"thread_cache_free_bytes" : 1057800,
"aggressive_memory_decommit" : 0,
...
}
}
記憶體使用優化
合理配置 WiredTiger cacheSizeGB
- 如果一個機器上只部署 Mongod,mongod 可以使用所有可用記憶體,則是用預設配置即可。
- 如果機器上多個mongod混部,或者mongod跟其他的一些程式一起部署,則需要根據分給mongod的記憶體配額來配置
cacheSizeGB
,按配額的60%左右配置即可。
控制併發連線數
TCP連線對 mongod 的記憶體開銷上面已經詳細分析了,很多同學對併發有一定誤解,認為「併發連線數越高,資料庫的QPS就越高」,實際上在大部分資料庫的網路模型裡,連線數過高都會使得後端記憶體壓力變大、上下文切換開銷變大,從而導致效能下降。
MongoDB driver 在連線 mongod 時,會維護一個連線池(通常預設100),當有大量的客戶端同時訪問同一個mongod時,就需要考慮減小每個客戶端連線池的大小。mongod 可以通過配置 net.maxIncomingConnections
配置項來限制最大的併發連線數量,防止資料庫壓力過載。
配置 SWAP
官方文件上的建議配置一下swap,避免mongod因為記憶體使用太多而OOM。
開啟 SWAP 與否各有優劣,SWAP開啟,在記憶體壓力大的時候,會利用SWAP磁碟空間來緩解記憶體壓力,此時整個資料庫服務會變慢,但具體變慢到什麼程度是不可控的。不開啟SWAP,當整體記憶體超過機器記憶體上線時就會觸發OOM killer把程式幹掉,實際上是在告訴你,可能需要擴充套件一下記憶體資源或是優化對資料庫的訪問了。
是否開啟SWAP,實際上是在「好死」與「賴活著」的選擇,個人覺得,對於一些重要的業務場景來說,首先應該為資料庫規劃足夠的記憶體,當記憶體不足時,「及時調整擴容」比「不可控的慢」更好。
其他
- 儘量減少記憶體排序的場景,記憶體排序一般需要更多的臨時記憶體
- 主備節點配置差距不要過大,備節點會維護一個buffer(預設最大256MB)用於儲存拉取到oplog,後臺從buffer裡取oplog不斷重放,當備同步慢的時候,這個buffer會持續使用最大記憶體。
- 控制集合及索引的數量,減少databse管理後設資料的記憶體開銷;集合、索引太多,後設資料記憶體開銷是一方面的影響,更多的會影響啟動載入的效率、以及執行時的效能。
參考:https://yq.aliyun.com/articles/685044?spm=a2c4e.11153940.blogrightarea60553.9.2a0e2e23YE5Bln
相關文章
- 關於redis記憶體分析,記憶體優化Redis記憶體優化
- Linux效能優化:記憶體使用情況分析Linux優化記憶體
- 實踐App記憶體優化:如何有序地做記憶體分析與優化APP記憶體優化
- 2.記憶體優化(二)優化分析記憶體優化
- mongodb之使用explain和hint效能分析和優化MongoDBAI優化
- Android記憶體優化(五)詳解記憶體分析工具MATAndroid記憶體優化
- Android記憶體優化——記憶體洩露檢測分析方法Android優化記憶體洩露
- Android記憶體分析和調優(上)Android記憶體
- Android記憶體分析和調優(中)Android記憶體
- Android記憶體分析和調優(下)Android記憶體
- 記憶體優化策略記憶體優化
- UIImage 記憶體優化UI記憶體優化
- PHP記憶體優化PHP記憶體優化
- 記憶體優化技巧記憶體優化
- Android 效能優化之旅3 記憶體分析工具的使用(下)Android優化記憶體
- iOS 使用Instruments優化記憶體效能iOS優化記憶體
- Redis 記憶體使用優化與儲存Redis記憶體優化
- 記憶體洩露例項分析 -- Android記憶體優化第四彈記憶體洩露Android優化
- Android效能優化篇之記憶體優化--記憶體洩漏Android優化記憶體
- MongoDB 如何使用記憶體?為什麼記憶體滿了?MongoDB記憶體
- MongoDB如何使用記憶體?為什麼記憶體滿了?MongoDB記憶體
- 記憶體使用總結篇 -- Android 記憶體優化第五彈記憶體Android優化
- Android效能優化 - 記憶體優化Android優化記憶體
- 效能優化——記憶體洩漏(2)工具分析篇優化記憶體
- Android 記憶體優化(二)DVM 和 ART 的 GC 日誌分析Android記憶體優化GC
- 二、Android效能優化之記憶體洩露分析及工具使用Android優化記憶體洩露
- 記憶體優化相關記憶體優化
- Android記憶體優化Android記憶體優化
- Python 記憶體優化Python記憶體優化
- Android 記憶體優化Android記憶體優化
- app 的記憶體優化APP記憶體優化
- 1.記憶體優化(一)記憶體洩漏記憶體優化
- Android記憶體優化之記憶體快取Android記憶體優化快取
- Android記憶體優化之static使用篇Android記憶體優化
- Mongodb記憶體管理和使用情況情況查詢MongoDB記憶體
- Android 效能優化之記憶體優化Android優化記憶體
- 分析並優化 Android 應用記憶體佔用優化Android記憶體
- 效能優化——記憶體洩漏(3)程式碼分析篇優化記憶體