往期分享
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 記憶體高問題 MongoDB 磁盤IO高問題概述
阿裡雲資料庫MongoDB的空間使用率是一個非常重要的監控名額,如果執行個體的存儲空間完全打滿,将會直接導緻執行個體不可用。一般來說,當一個MongoDB執行個體的存儲空間使用比例達到80-85%以上時,就應及時進行處理,要麼降低資料庫實際占用空間的大小,要麼對存儲空間進行擴容,以避免空間打滿的風險。
然而,阿裡雲資料庫MongoDB的空間使用情況分析并不簡單,本文将由淺入深幫您檢視,分析和優化雲資料庫MongoDB的空間使用。
檢視空間使用
副本集模式
部署架構為副本集模式下,提供有多種檢視空間使用的方法,您可以根據自身需求,由淺入深了解MongoDB的空間使用情況。
總體概覽
在MongoDB控制台的“基本資訊”頁中會顯示執行個體的總存儲空間使用大小,但這裡隻有目前的空間的總使用率,沒有具體的各類資料分别占用空間大小的資訊,也沒有空間使用的曆史資訊,如下圖:

監控圖分析
MongoDB副本集由多種角色組成,一個角色可能對應一個或多個實體節點。阿裡雲MongoDB對使用者暴露Primary和Secondary節點,另外還提供有隻讀執行個體的角色。可以通過點選"監控資訊",選擇對應的角色檢視MongoDB空間相關的監控情況,如下圖:
其中一個MongoDB實體節點的空間使用由兩大塊組成,分别為data_size和log_size,其中這裡的data_size是資料磁盤使用空間(不包括local庫),主要包括collection打頭的資料實體檔案,index打頭的索引實體檔案,以及少部分中繼資料實體檔案,比如WiredTiger.wt。
log_size包括local庫的實體大小,mongodb運作日志大小,以及少部分的審計日志大小。
ins_size=data_size+log_size
詳細分析
如果需要深入分析副本集中庫或表的空間詳細占用,除了MongoDB自帶的db.stats(),db.$collection_name.stats()以外,我們推薦使用阿裡雲MongoDB控制台的提供的"CloudDBA-空間分析",通過"CloudDBA-空間分析",您可以實作以下目的
-
- 檢視庫表空間概覽,日均增長量,預測可用天數。
- 檢視異常庫表空間使用。
- 詳細業務表的空間使用,包括索引和資料邏輯大小,壓縮率分析,平均行長等更多内容。
更多的CloudDBA-空間分析内容參考:
https://help.aliyun.com/document_detail/162422.html更多mongodb官方提供的分析指令詳解參考:
- https://docs.mongodb.com/manual/reference/method/db.stats/
- https://docs.mongodb.com/manual/reference/method/db.collection.stats/
- https://docs.mongodb.com/manual/reference/method/db.collection.storageSize/
- https://docs.mongodb.com/manual/reference/method/db.collection.totalIndexSize/
- https://docs.mongodb.com/manual/reference/method/db.collection.totalSize/
分片叢集
在分片叢集的部署模式下,由于一個叢集下可能存在多個Shard,沒有整體空間使用率的概念。是以控制台不再提供"基本資訊"中的空間使用率。
阿裡雲MongoDB的監控資訊中詳細提供了叢集組建mongos路由節點,config server配置節點以及shard節點的空間使用率。不過通常來說,mongos和config server節點不會成為空間瓶頸,建議選擇忽略,直接檢視各個shard中各個角色的空間使用情況,如下圖:
分片叢集的空間使用的詳細檢視方法與副本集模式略有不同,需要逐個登入各個shard通過指令檢視,在每個分片上的空間使用情況詳情就與副本集情況完全一樣。
另外一方面,分片叢集中的空間問題除了空間使用率以外,還存在非常大量的"各shard空間使用不均勻"問題,後者非常難以分析,目前CloudDBA暫不支援。本文後續章節會重點帶您了解和深入分析“不同分片下空間使用不均衡”,“同一副本集下各個角色空間使用不均衡”。
空間問題的疑難雜症分析和解決方法
空間問題上漲概要分析和一般解決思路
當收到磁盤空間報警後,一般的分析和解決思路如下:
- 确認目前的空間使用值,确認目前空間使用中各個庫表的詳細占用情況。
- 确認引起空間增長的主要源頭,比如是日志類增長,還是具體的某個業務表寫入暴漲。
- 确認目前的增長是否符合預期,針對不符合預期的大量寫入場景做應用分析。
- 确認目前的資料是否存在大量碎片,能否通過回收碎片的方式回收空間。
- 确認是否需要做磁盤空間擴容,或者部署定時曆史資料删除或TTL Index。
- 曆史資料大量删除完成,通過compact或者重做副本集的方式回收碎片空間。
compact方法和compact期間對執行個體的影響
compact 一個集合,會加集合所在DB的互斥寫鎖,會導緻該DB上所有的讀寫請求都阻塞,因為 compact 執行的時間可能很長,跟集合的資料量相關,是以強烈建議在業務低峰期執行,避免影響業務。
compact方法很簡單,建議優先在備庫上執行,并通過主備切換的方式來減少compact期間對業務的影響,指令為db.runCommand({compact: "collectionName"})。
另外,MongoDB4.4以後的官方版本,Compact指令将不再阻塞業務讀寫,更多compact的指令的使用方法和限制參考:
- https://docs.mongodb.com/manual/reference/command/compact/
- https://mongoing.com/archives/26907
- https://help.aliyun.com/document_detail/96530.html
compact無效
如前文所述,在大量remove的場景下我們會需要使用compact來回收碎片空間。然而在極端場景下,compact操作雖然提示成功了,但實際上磁盤空間并沒有回收,這應該算是一個BUG或者是MongoDB在compact設計上的缺陷,并不是隻要存在碎片就一定能回收成功。compact的基本原理并不是立馬開辟新的空間存放資料來替換原來的檔案,而是将資料不斷地往前面的空間空洞挪動,是以在某些場景下雖然存在空間空洞,但内部的compact算法并不能保證肯定可以複用這些空洞。針對這種compact失效的場景,如果糾結于空間使用,可以通過重建副本的方式解決。
還有一種場景,在MongoDB3.4以前的版本,compact或許還存在一個BUG:在大量删除資料後,compact無法回收索引檔案,隻對資料檔案生效。這個可以通過使用指令db.$table_name.stats().indexSizes或者直接檢視索引實體檔案大小确認。針對這種情況,建議将核心版本更新至3.4以上。
journal log過大導緻主備空間差距巨大
在極端情況下,journal log可能觸發bug導緻空間無限上漲,可以通過MongoDB運作日志檢視到類似以下内容:
2019-08-25T09:45:16.867+0800 I NETWORK [thread1] Listener: accept() returns -1 Too many open files in system
2019-08-25T09:45:17.000+0800 I - [ftdc] Assertion: 13538:couldn't open [/proc/55692/stat] Too many open files in system src/mongo/util/processinfo_linux.cpp 74
2019-08-25T09:45:17.002+0800 W FTDC [ftdc] Uncaught exception in 'Location13538: couldn't open [/proc/55692/stat] Too many open files in system' in full-time diagnostic data capture subsystem. Shutting down the full-time diagnostic data capture subsystem.
該bug的觸發條件為主控端的open files達到設定上限,導緻MongoDB内部的log server清理線程中斷,4.0以前的官方版本均有該問題,如果有遇到您可以将核心版本更新到4.0以上,或者可以通過重新開機mongod程序臨時解決,具體的bug連結參考:
https://jira.mongodb.org/browse/WT-4083備庫延遲和增量備份可能導緻Secondary日志空間持續增長
預設的官方MongoDB,oplog是一個固定集合,大小基本是固定的,主備之間的實體檔案大小不會有太大差異。阿裡雲MongoDB出于oplog經常過期導緻的節點recovering狀态,開發了oplog自适應特性。也就是說,在極端場景出現主備延遲的情況下,實際oplog可以使用的大小不再受限于配置檔案定義的固定集合大小,理論上可以達到使用者申請磁盤容量的20%。這就造成了一個問題,當備庫延遲恢複後,oplog之前占用的實體空間并不會回縮。
另外,處于備份和恢複效率的考慮,阿裡雲MongoDB使用實體備份的方式在Hidden備份mongodb執行個體,期間會涉及到大量的chepoint導緻占用了更多的資料和日志空間。
針對以上場景,當空間占用量不是特别大的話通常建議直接忽略即可。也可以根據需要對oplog做單獨的compact操作,compact期間會阻塞所有的寫操作。方法如下:
db.grantRolesToUser("root", [{db: "local", role: "dbAdmin"}])
use local
db.runCommand({ compact: "oplog.rs", force: true })
不同分片之間的空間使用不均衡
sharding key類型選擇不合理
在一個分片叢集中,片鍵類型的選擇至關重要,一般會使用hash分片或者ranged分片兩種類型。通常情況下,在磁盤均衡度方面,hash分片的政策會比ranged好很多,因為根據不同的key值,MongoDB通過内部的哈希函數可以使得資料均勻地分布在不同地分片上,如下圖所示:
而range分片一般是根據key的大小範圍進行資料分布,是以往往會造成這樣的一個現象:新插入的資料在一個熱點的chunk上,不但會引起該chunk所在的shard磁盤IO過高,也會帶來短期資料不均勻的場景。如下圖所示,所有的資料寫入chunk C所在分片,當chunk C寫滿以後會在本分片中split出新的chunk,後續通過叢集負載均衡器Balancer遷移chunk,但這個遷移需要耗費大量的時間和IO,在一個高并發寫入的場景下,資料的負載均衡速度可能跟不上資料寫入速度,進而造成分片之間資料容量不均的問題。在這種使用場景下,不建議使用range分片政策。
更多的Sharding Key類型介紹參考:
- https://docs.mongodb.com/manual/core/sharding-shard-key/
- https://docs.mongodb.com/manual/core/hashed-sharding/
- https://docs.mongodb.com/manual/core/ranged-sharding/
sharding key字段選擇不合理
通過sh.status()可以看到各個分片上的chunk數量基本一緻,但實際上絕大部分資料都隻存在部分chunk上,導緻這些熱點chunk所在的分片資料量遠大于其他分片,通過檢視MongoDB運作日志可以檢視到明顯的告警資訊:
2019-08-27T13:31:22.076+0800 W SHARDING [conn12681919] possible low cardinality key detected in superHotItemPool.haodanku_all - key is { batch: "201908260000" }
2019-08-27T13:31:22.076+0800 W SHARDING [conn12681919] possible low cardinality key detected in superHotItemPool.haodanku_all - key is { batch: "201908260200" }
2019-08-27T13:31:22.076+0800 W SHARDING [conn12681919] possible low cardinality key detected in superHotItemPool.haodanku_all - key is { batch: "201908260230" }
mongos負載均衡主要考慮的是各個shard的chunk數量保持相當,就認為資料是均衡的,是以就會出現以上的極端場景:雖然各個shard數量相當,但實際資料嚴重傾斜。因為一個chunk内shardKey幾乎完全相同但又觸發到64M的chunk分裂門檻值,這時就會分裂出一個空的chunk。久而久之,雖然chunk的數量變多了并且完成了chunk的遷移,但實際上遷移走的chunk都是空的chunk,造成了chunk數量均衡但實際資料不均衡的情況。(這應該是Balancer在遷移chunk時基于代價的考慮,認為空chunk遷移代價更低,是以優先選了空的chunk遷移)
更多split的介紹參考:
- https://docs.mongodb.com/manual/core/sharding-data-partitioning/
- https://docs.mongodb.com/manual/tutorial/split-chunks-in-sharded-cluster/
針對這種情況,隻能在架構上重新設計,選擇合适的區分度較高的列作為sharding key。
部分db未做sharding
MongoDB分片叢集允許部分db做sharding,部分db不做shading。那麼必然會帶來這樣的一個問題:不做shading的db的資料必然隻能存在一個分片上,如果該db資料量很大,可能會造成該分片的資料量遠大于其他分片。
除了上述情況,比較容易引起該問題的使用場景通常是因為從一個源端mongos叢集導入到一個新的mongos叢集,而邏輯導入過程中忽略了一步:實作在目标端mongos叢集做好sharding設計,因為邏輯導入并不會自動做sharding設計,這一步需要事先做好。
針對這種問題,我們建議:
-
- 如果是因為目标叢集初始化導入,導入之前做好分片設計。
- 如果不做sharding的庫很多且資料量基本相當,Mongos提供有movePriamy指令将指定db遷移到指定分片。
- 如果存在某個db的資料量極大且未做sharding,建議對其做sharding設計或者将其拆分出來當作單一的副本集對待。
- 即使出現這種情況,如果磁盤總量足夠充裕,建議忽略。
大規模的movechunk操作可能引起分片的磁盤占用不均
movechunk的本質是向目标端shard寫入資料後remove源端資料。預設情況下,remove操作不會釋放空間,因為針對wiredTiger引擎來說每個表都有獨立的資料檔案和索引檔案,如果該檔案不删除,總的磁盤空間就不可能回縮。通常最容易引起該問題的操作為:之前的sharding叢集中未做shard設計,運作一段時間後才做了Sharding。
從原理上說movechunk引起的空間碎片和大規模delete一樣,是以針對出現大量movechunk或者remove文檔的情況,可以針對該分片進行compact操作來回收碎片空間,正常情況下compact後資料會進行重組進而回收檔案的碎片空間。
更多movechunk的介紹參考:
- https://docs.mongodb.com/manual/tutorial/migrate-chunks-in-sharded-cluster/
- https://docs.mongodb.com/manual/tutorial/manage-sharded-cluster-balancer/
阿裡雲MongoDB産品在空間使用優化上的規劃
計算存儲分離
目前的阿裡雲MongoDB單個實體節點最大支援的存儲空間是3T,如果單執行個體或副本集模式下的節點資料量超過3T就必須業務拆分或者更新至分片叢集,而且規格擴容涉及到資料遷移會導緻耗時較長。
阿裡雲MongoDB在副本集部署模式下即将支援雲盤以實作計算存儲分離。在雲盤存儲下,單個節點的資料存儲上限将達到數十T,以及雲原生架構帶來的分鐘級擴容能力。
ECS快照備份
在高版本的阿裡雲MongoDB執行個體中,由于oplog多線程回放技術的越發成熟,基本上不會再有備庫延遲問題,是以阿裡雲MongoDB核心層面不再對oplog做定制化改造,與官方的"固定集合"形态保持一緻,備庫延遲引起的oplog放大問題通過更新至最新版核心即可得到解決。
另外,目前阿裡雲MongoDB正在研發采用ECS快照技術備份MongoDB執行個體,當故障發生時可繞過耗時的OSS實體備份下載下傳,達到分鐘級的恢複能力。并且使用ECS快照備份後,傳統實體備份方式可能導緻Hidden節點空間上漲的問題也會得到解決。
Compact指令的核心改造優化
MongoDB4.4的Compact指令并不會阻塞讀寫,阿裡雲MongoDB在核心層面将該patch移植至雲資料庫 MongoDB 3.4及以上版本,通過DAS控制台點選的方式實作空間碎片整理的産品化。