萬億級數據庫 MongoDB 集羣性能數十倍提升優化實踐核心 17 問詳細解答
關於作者
前滴滴出行技術專家,現任 OPPO 文檔數據庫 mongodb 負責人,負責數萬億級數據量文檔數據庫 mongodb 內核研發、性能優化及運維工作,一直專注於分佈式緩存、高性能服務端、數據庫、中間件等相關研發。後續持續分享《MongoDB 內核源碼設計、性能優化、最佳運維實踐》,Github 賬號地址:https://github.com/y123456yz
- 性能優化有推薦的分析和監控工具麼?
mongodb 常用性能分析主要如下:
1.1 mongodb 自帶性能分析工具
** mongodb 官方對外工具 mongostat**
命令行使用方法 (ip:port 爲代理 ip 和端口):
mongostat -h ip:port -u 用戶名 -p 密碼 --authenticationDatabase=admin --discover
mongostat 工具帶上 --discover,可以把所有分片節點信息一起打印出來,直觀查看整個集羣所有節點實例級監控信息。mongostat 統計信息中最核心的幾個影響性能的統計項:
- ****dirty:****存儲引擎髒數據比例,默認該值爲 5% 的時候,wiredtiger 存儲引擎自帶的 evict 現成開始選擇髒數據 page 淘汰到磁盤;如果該值達到 20%,客戶端請求對應 mongodb 處理現成將會選擇髒數據 page 淘汰到磁盤,等 page 淘汰騰出內存空間後,纔會處理客戶端請求的 DB 訪問,所以如果閥值達到 20% 客戶端訪問將會變慢。
- ****used:****存儲引擎 cacheSize 配置佔用百分比,如果配置 cacheSize=10G,存儲引擎實際使用了 7G,則 used 贊比爲 70%。當該統計值達到 80%,evict 線程將會觸發選擇漲數據淘汰,如果這個佔比提高到 95%,用戶請求線程將會觸發淘汰,客戶端請求將會變慢。
- ****qrw arw:****等待隊列數,如果該值越大,說明會引起客戶端請求排隊處理。一般該值會再 dirty 佔比超過 20%,used 佔比過高超過 95%,或者磁盤 IO 慢會出現。
- ****vsize res:****虛擬內存和物理內存真實佔用,如果 vsize 過高,遠遠超過 res,或者 res 過高,遠遠超過 cachesize 配置,則說明內存碎片,pageheap 等問題,這時候可以通過加速 tcmalloc 內存釋放速率來解決問題。
- 慢日誌分析
通過以下命令分析日誌文件
1)找出文件末尾 1000000 行中存在掃表的操作,不包含 oplog,getMore
tail mongod.log -n 1000000 | grep ms |grep COLLSCAN |grep -v "getMore" | grep -v "oplog.rs"
2)找出文件末尾 1000000 行中所有的慢日誌,不包含 oplog,getMore
tail mongodb.log -n 1000000 |grep ms | grep op_msg | grep find | grep -v "oplog.rs" |grep -v "getMore"
3.)找出文件末尾 1000000 行中執行時間 1-10s 的請求,不包含 oplog,getMore
tail mongodb.log -n 1000000 |grep ms | grep op_msg | grep find | grep -v "oplog.rs" |grep -v "getMore" | egrep [1-9][0-9][0-9][0-9]ms
4)currentOp 正在執行的慢操作分析
慢日誌只有當請求執行完畢纔會,如果一個表很大,一個查詢掃表,則整個執行過程可能需要數小時,可能還沒記錄慢日誌,則可以通過如下命令獲取當前執行時間超過 5s 的所有請求,查詢請求,command 請求:
db.currentOp({"secs_running":{"$gt":5}})
db.currentOp({"secs_running":{"$gt":1}, "op":"query"})
db.currentOp({"secs_running":{"$gt":5}, "op":"command"})
kill 查詢時間超過 5s 的所有請求:
db.currentOp().inprog.forEach(function(item){if(item.secs_running > 5 )db.killOp(item.opid)})
- 節點存儲引擎監控信息
db.serverStatus().wiredTiger 可以獲取 mongod 節點對應存儲引擎的各自詳細統計信息,裏面可以完整獲取時延消耗在存儲引擎哪一個環節。
下面是空餘時間分析的 wiredtiger 源碼,分析不是很完整,後續等 mongodb server 層單機、複製集、分片等完整模塊化分析後,會回頭繼續分析。
1.2 操作系統性能瓶頸分析
系統層面性能分析工具主要有:top、iostat、pstak、ptress、perf、iotop、isof 等,具體請參考對應工具說明。
1.3 開源 mongodb 詳細監控套記
** 開源方案可以參考以下組件:**
** Grafana+Prometheus+node_exporter+mongodb_exporter**
- 服務端組件:
Prometheus #服務端
Grafana #前端展示 - 客戶端組件:
node_exporter
mongodb_exporter
2. 會話加標籤是怎麼指定服務器?
**** 舉一個例子形象說明:****我們把用戶分爲三組,20 歲以下(junior),20 到 40 歲(middle)和 40 歲以上(senior),按照下面的幾條命令執行以後,我們的數據會按照用戶年齡段拆分成若干個 chunk,並分發到不同的 shard cluster 中。
如果對下面的命令不熟悉,可以查看 MongoDB 官方文檔關於 Shard Zone/Chunk 的解釋。
sh.addShardTag('shard01', 'junior')
sh.addShardTag('shard02', 'middle')
sh.addShardTag('shard03', 'senior')
sh.addTagRange('test.users', {'user.age': MinKey}, {'user.age':20}, 'junior')
sh.addTagRange('test.users', {'user.age': 21}, {'user.age':40}, 'middle')
sh.addTagRange('test.users', {'user.age': 41}, {'user.age': MaxKey}, 'senior')
通過上面的 6 個命令給'test 庫的 user 表加標籤,20 以下對應標籤爲'junior',21-40 對應標籤爲'middle',41 以上對應標籤爲'senior'。同時把'junior'標籤分配給'shard01',也就是 0-20 歲的 user 會全部寫到'shard01',21-40 歲的 user 會全部寫到'shard01',41 歲以上的 user 會全部寫到'shard01'。
這樣就可以解決跨機房寫的問題,只要對應分片主節點在對應機房即可。
3. 髒數據比例多少算高?
默認 20% 算高,如果髒數據比例持續性超過 20%,可以試着提高 wiredtiger 存儲引擎後臺淘汰線程數:
db.adminCommand({ setParameter : 1, "wiredTigerEngineRuntimeConfig" : "cache_size=35GB, eviction=(threads_min=4, threads_max=12)"})
4. 寫分開,會有時延嗎,是不是有一致性問題?
一致性默認完全由 mongodb 複製集自帶的主從同步機制來保證最終一致性,不存在雙向同步兩集羣的一致性問題。
如果要實現複製集中主從節點的強一致性,可以通過客戶端配置 writeconcern 策略來解決。
5. 比如想定位詳細的慢查詢呢**?******
和問題 1 雷同,可以通過分析 currentop、日誌文件或者 system.profile 慢日誌表來獲取詳細的慢日誌信息。建議平臺化收集慢日誌,這樣界面展示分析更加直觀。
6. 如何快速定位 Mongodb 的問題發生在集羣中的哪些節點? 在啓用讀寫分離的情況下?
主要通過如下幾個步驟來分析:
- db.serverStatus().opLatencies 監控 mongod 實例時延
- 如果由運維研發能力,可以自己收集時延展示,如果沒有。則可以藉助開源工具系統實現,參考《1.3 開源 mongodb 詳細監控套記》
- 充分利用 mongostat 監控集羣所有節點實時髒數據、隊列、內存信息
- 參考《1.1 mongodb 自帶性能分析工具》
- 慢日誌分析, 參考《比如想定位詳細的慢查詢呢?》
7. 楊老師,就您經驗來講,您覺得如何保證 MongoDB 的安全性呢?
安全性方面主要由以下幾方面保證:
- 賬號鑑權認證,一個庫一個賬號
- readWrite 權限去除刪庫、刪表等危險操作權限
- 不同業務不混用同一個集羣
- 啓用黑白名單功能
- 我司 mongodb 內核增加審計、流量控制、危險操作控制等功能 (注:部分功能是 mongodb 企業級功能,需要付費,可以使用 percona mongodb 版本)
- 數據定期備份,我司 mongodb 內核增加有熱備功能。
注意:如果數據量很大,建議不要使用 mongodump 備份,mongodump 備份會很慢,同時通過 mongorestore 恢復也是一條數據一條數據恢復,同樣很慢。如果有內核研發能力,可以增加熱備功能。如果沒有內核研發能力,可以通過如下步驟備份:1. 隱藏節點;2. 鎖庫;3. 拷貝數據文件。或者採用 percona mongodb 版本來備份。
8. mysql 和 mongodb 雙寫的話怎麼保證事務呢
mysql 我不是很瞭解,mongodb 不推薦搭兩集羣雙向同步來備份,直接利用 mongodb 原生的複製集功能來完成多活容災,成本、性能、一致性都可以得到保證。即使是 4.2 分佈式事務功能也可以直接利用 mongodb 自身的機制來保證,具體方案參考我在 Qcon 全球軟件開發大會的分享:
萬億級數據庫 MongoDB 集羣性能優化及機房多活容災實踐
9. hashnum 的方式來講數組中的方式來拆分成多個表? 沒太明白
分享的案例 2:萬億級數據量 mongodb 集羣性能數倍提升優化實踐,不是拆分數據到多個表,而是把一條數據 (該數據保護一個數組,數組中包含數百萬個子文檔) 通過 hash 的方式散列爲多條數據。也就是之前數百萬個子文檔歸屬於一條數據,現在把他拆分爲歸屬到多條數據。
通過這樣合理的數據合併和拆分,最終平衡磁盤 IO,實現讀和寫達到一種平衡態,既能滿足業務讀需求,同時也能滿足業務寫需求。
10. 對分片鍵設計要求高嗎?
分片集羣片建選擇非常重要,對分片模式集羣性能起着核心至關重要的作用,分片集羣片建選擇遵循以下幾個原則:
11. 首先需要考慮集羣部署是否需要分片?
只有以下情況才需要分片功能:1. 數據量太大,一個分片撐不住;2. 寫流量太大,寫只能走主節點,一個主節點撐不住,需要擴分片分擔寫流量。
12. 片建選擇原則?
片建選擇原則如下: 1. 保證數據儘量離散;2. 儘量保證更新和查詢到同一個分片 (如果同一次更新或者查詢到多個分片,只要任何一個分片慢,該操作都會慢;同時部分查詢會進一步加劇代理聚合負擔)。
此外,如果查詢注意是範圍查詢,建議選擇範圍分片,這樣有利於範圍數據集中到同一個分片。
13. 大表分片後,寫表還是會跨機房嗎?
機房多活打標籤方式解決跨機房寫問題,同樣可以對對應 tag 表啓用分片功能,保證數據到指定的多個分片,每個分片主節點在指定機房,可以解決跨機房問題。詳情參考:《會話加標籤是怎麼指定服務器?》
14. 老師您好,想請問下:MongoDB 適合做商城 app 數據庫嗎?一般在哪些場景使用呢?謝謝!
個人覺得完全可以滿足要求,同時還有利於業務的快速迭代開發。mongodb 天然的模式自由 (加字段方便)、高可用、分佈式擴縮容、機房多活容災機制,可以快速推進業務迭代開發。以我的經驗,至少 90% 以上使用 mysql 的場景,mongodb 同樣可以滿足要求。mongodb 唯一缺點可能是生態沒 mysql 健全,研究 mongodb 的人相當少。
15. 老師能講講你們容量預警是怎麼做的嗎?
**** 容量水位我們分爲以下幾種:****
- 磁盤容量限制
當一個分片中磁盤使用率超過 80%,我們開始擴容增加分片。
- 流量超過閥值
讀寫流量閥值水位如下:1. 如果是分片的寫流量持續性超過 3.5W/s(ssd 服務器) 則擴容分片;2. 如果是讀流量單節點持續性超過 4W/s(ssd 服務器,所有讀走磁盤 IO),則擴容從節點來解決讀流量瓶頸,注意需要配置讀寫分離。
- CPU 閥值
我們所有實例容器部署,實例如果 CPU 使用率持續性超過 80%,考慮增加容器 CPU。
16. 數據一致性在遷移過程中同步你們是怎麼保證的呢
如果通過 mongoshake 等工具遷移集羣,需要提前關閉 blance 功能,否則無法解決一致性問題。
我們線上集羣只有把數據從集羣遷移到另一個集羣的時候纔會使用 mongoshake,我們機房多活不是多個集羣雙寫方式,而是同一個集羣,通過夫直接的主從同步拉取 oplog 機制實現一致性,所以不存在一致性問題。可以參考 萬億級數據庫 MongoDB 集羣性能優化及機房多活容災實踐
17. 我們數據體量不太大,主要是雜,這種環境想做好數據治理,老師你建議把重點放在哪些方面?然後有沒有一些比較常見的坑?
數據量不大,比較雜的場景,一般集羣搞一個複製集即可滿足要求,無需分片模式部署。
我猜測你們的比較雜可能是利用 mongodb 的模式自由,造成每條數據的字段各不相同,數據長度大小各不一致。建議在使用模式自由這一功能的時候,一定不要” 濫用”、” 亂用”,在使用時代碼邏輯需要簡單控制。我重節線上遇到的對模式自由的” 濫用”、” 亂用” 引起的集羣問題:
- 同一個表數據字段各不相同,建議同一個表所有數據的字段保持一致,即使新數據增加字段也需要在老數據中增加該字段,保持字段一致。
- 同一個表的數據的字段控制在 50 個 KV 以內,這樣對應更新、查詢等性能分析有利,減少磁盤 IO 消耗。
- 如果數據字段過多,查詢的時候不要返回所有字段,只獲取對本次查詢有用的字段,減少忘了 IO 開銷。
- 數組別亂用,數組中的文檔保持格式統一。
- 數組中的子文檔如果需要查詢指定字段,一定記得對數組中嵌套的字段添加子索引。
- 數組字段中的文檔一定要控制在一定範圍,避免該數組過大,數組過大有遍歷、磁盤 IO 過高等問題。
- 嵌套子文檔層數不宜過多。
- ......
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://my.oschina.net/u/4087916/blog/4956301