MongoDB 最佳實踐

關於安全

爲MongoDB集羣啓用認證鑑權

MongoDB服務器在默認安裝下不啓用鑑權。這意味着每一個人均可以直接鏈接到mongod實例並執行任意數據庫操做。html

爲不一樣用戶分配不一樣的角色權限

MongoDB支持按角色定義的權限系統。你應該基於「最少權限」準則,顯式的爲用戶分配僅須要的相應權限。mongodb

使用中央鑑權服務器

儘量使用LDAP、Kerbero之類的中央鑑權服務器,並使用強口令策略。數據庫

爲須要訪問MongoDB的應用服務器建立白名單(防火牆配置)

若是你的服務器有多個網卡,建議只在內網的IP上監聽服務。緩存

對敏感數據使用加密引擎

MongoDB企業版支持存儲加密,對涉及到客戶的敏感數據應該使用加密引擎來保護數據。安全

關於部署

至少使用3個數據節點的複製集

MongoDB的建議最小部署是3個數據節點構成的複製集。複製集能夠提供如下優勢:服務器

  • 系統99.999% 高可用
  • 自動故障切換
  • 數據冗餘
  • 容災部署
  • 讀寫分離

不用太早分片

分片能夠用來擴展你係統的讀寫能力,可是分片也會帶來很多新的挑戰好比說管理上的複雜度,成本的增長,選擇合適片鍵的挑戰性等等。通常來講,你應該先窮盡了其餘的性能調優的選項之後纔開始考慮分片,好比說,索引優化,模式優化,代碼優化,硬件資源優化,IO優化等。網絡

選擇合適的分片數

分片的一些觸發條件爲:併發

  • 數據總量太大,沒法在一臺服務器上管理
  • 併發量過高,一臺服務器沒法及時處理
  • 磁盤IO壓力太大
  • 單機系統內存不夠大,沒法裝下熱數據
  • 服務器網卡處理能力達到瓶頸
  • 多地部署狀況下但願支持本地化讀寫 取決於你分片的觸發條件,你能夠按照總的需求 而後除以每一臺服務器的能力來肯定所需的分片數。

爲每一個分片部署足夠的複製集成員

分片之間的數據互相不復制。每一個分片的數據必須在分片內保證高可用。所以,對每個分片MongoDB要求至少部署3個數據節點來保證該分片在絕大部分時間都不會由於主節點宕機而形成數據不可用。異步

選擇合適的片鍵

在分片場景下, 最重要的一個考量是選擇合適的片鍵。選擇片鍵須要考慮到應用的讀寫模式。一般來講一個片鍵要麼是對寫操做優化,要麼是對讀操做優化。要根據哪一種操做更加頻繁而進行相應的權衡。分佈式

  • 片鍵值應該具備很高的基數,或者說,這個片鍵在集合內有不少不一樣的值,例如_id就是一個基數很高的片鍵由於_id值不會重複
  • 片鍵通常不該該是持續增加的,好比說timestamp就是個持續增加的片鍵。此類片鍵容易形成熱分片現象,即新的寫入集中到某一個分片上
  • 好的片鍵應該會讓查詢定向到某一個(或幾個)分片上從而提升查詢效率。通常來講這個意味着片鍵應該包括最經常使用查詢用到的字段
  • 好的片鍵應該足夠分散,讓新的插入能夠分佈到多個分片上從而提升併發寫入率。
  • 可使用幾個字段的組合來組成片鍵,以達到幾個不一樣的目的(基數,分散性,及查詢定向等)

關於系統

使用SSD 或RAID10 來提升存儲IOPS能力

MongoDB是一個高性能高併發的數據庫,其大部分的IO操做爲隨機更新。通常來講本機自帶的SSD是最佳的存儲方案。若是使用普通的硬盤,建議使用RAID10條帶化來提升IO通道的併發能力。

爲Data和Journal/log使用單獨的物理卷

MongoDB不少的性能瓶頸和IO相關。建議爲日誌盤(Journal和系統日誌)單獨設定一個物理卷,減小對數據盤IO的資源佔用。 系統日誌能夠直接在命令行或者配置文件參數內指定。Journal日誌不支持直接指定到另外的目錄,能夠經過對Journal目錄建立symbol link的方式來解決。

使用XFS 文件系統

MongoDB在WiredTiger存儲引擎下建議使用XFS文件系統。Ext4最爲常見,可是因爲ext文件系統的內部journal和WiredTiger有所衝突,因此在IO壓力較大狀況下表現不佳。

WiredTiger下謹慎使用超大緩存

WiredTiger 對寫操做的落盤是異步發生的。默認是60秒作一次checkpoint。作checkpoint須要對內存內全部髒數據遍歷以便整理而後把這些數據寫入硬盤。若是緩存超大(如大於128G),那麼這個checkpoint時間就須要較長時間。在checkpoint期間數據寫入性能會受到影響。目前建議實際緩存設置在64GB或如下。

關閉 Transparent Huge Pages

Transparent Huge Pages (THP) 是Linux的一種內存管理優化手段,經過使用更大的內存頁來減小Translation Lookaside Buffer(TLB)的額外開銷。 MongoDB數據庫大部分是比較分散的小量數據讀寫,THP對MongoDB這種工況會有負面的影響因此建議關閉。 http://docs.mongoing.com/manual-zh/tutorial/transparent-huge-pages.html

啓用Log Rotation

防止MongoDB 的log文件無限增大,佔用太多磁盤空間。好的實踐是啓用log rotation並及時清理歷史日誌文件。

分配足夠的Oplog空間

足夠的Oplog空間能夠保證有足夠的時間讓你從頭恢復一個從節點,或者對從節點執行一些比較耗時的維護操做。假設你最長的下線維護操做須要H小時,那麼你的Oplog 通常至少要保證能夠保存 H 2 或者 H3 小時的oplog。

關閉數據庫文件的 atime

禁止系統對文件的訪問時間更新會有效提升文件讀取的性能。這個能夠經過在 /etc/fstab 文件中增長 noatime 參數來實現。例如: /dev/xvdb /data ext4 noatime 0 0 修改完文件後從新 mount就能夠: mount -o remount /data

提升默認文件描述符和進程/線程數限制

Linux默認的文件描述符數和最大進程數對於MongoDB來講通常會過低。建議把這個數值設爲64000。由於MongoDB服務器對每個數據庫文件以及每個客戶端鏈接都須要用到一個文件描述符。若是這個數字過小的話在大規模併發操做狀況下可能會出錯或沒法響應。 你能夠經過如下命令來修改這些值: ulimit -n 64000 ulimit -u 64000

禁止 NUMA

在一個使用NUMA技術的多處理器Linux 系統上,你應該禁止NUMA的使用。MongoDB在NUMA環境下運行性能有時候會可能變慢,特別是在進程負載很高的狀況下。

預讀值(readahead)設置

預讀值是文件操做系統的一個優化手段,大體就是在程序請求讀取一個頁面的時候,文件系統會同時讀取下面的幾個頁面並返回。這緣由是由於不少時候IO最費時的磁盤尋道。經過預讀,系統能夠提早把緊接着的數據同時返回。假設程序是在作一個連續讀的操做,那麼這樣能夠節省不少磁盤尋道時間。 MongoDB不少時候會作隨機訪問。對於隨機訪問,這個預讀值應該設置的較小爲好.通常來講32是一個不錯的選擇。你可使用下述命令來顯示當前系統的預讀值: blockdev --report 要更改預讀值,能夠用如下命令: blockdev --setra 32

使用NTP時間服務器

在使用MongoDB複製集或者分片集羣的時候,注意必定要使用NTP時間服務器。這樣能夠保證MongoDB集羣成原則之間正確同步。

關於索引

爲你的每個查詢創建合適的索引

這個是針對於數據量較大好比說超過幾十上百萬(文檔數目)數量級的集合。若是沒有索引MongoDB須要把全部的Document從盤上讀到內存,這會對MongoDB服務器形成較大的壓力並影響到其餘請求的執行。

建立合適的組合索引,不要依賴於交叉索引

若是你的查詢會使用到多個字段,MongoDB有兩個索引技術可使用:交叉索引和組合索引。交叉索引就是針對每一個字段單獨創建一個單字段索引,而後在查詢執行時候使用相應的單字段索引進行索引交叉而獲得查詢結果。交叉索引目前觸發率較低,因此若是你有一個多字段查詢的時候,建議使用組合索引可以保證索引正常的使用。 例如,若是應用須要查找全部年齡小於30歲的運動員: db.athelets.find({sport: "marathon", location: "sz", age: {$lt: 30}}}) 那麼你可能須要這樣的一個索引: db.athelets.ensureIndex({sport:1, location:1, age:1});

組合索引字段順序:匹配條件在前,範圍條件在後(Equality First, Range After)

以上文爲例子,在建立組合索引時若是條件有匹配和範圍之分,那麼匹配條件(sport: 「marathon」) 應該在組合索引的前面。範圍條件(age: ❤️0)字段應該放在組合索引的後面。

儘量使用覆蓋索引(Covered Index)

有些時候你的查詢只須要返回不多甚至只是一個字段,例如,但願查找全部虹橋機場出發的全部航班的目的地。已有的索引是: {origin: 1, dest: 1} 若是正常的查詢會是這樣(只須要返回目的地機場): db.flights.find({origin:"hongqiao"}, {dest:1}); 這樣的查詢默認會包含_id 字段,因此須要掃描匹配的文檔並取回結果。相反,若是使用這個查詢語句: db.flights.find({origin:"hongqiao"}, {_id:0, dest:1}); MongoDB則能夠直接從索引中取得全部須要返回的值,而無需掃描實際文檔(文檔可能須要從硬盤裏調入到內存)

建索引要在後臺運行

在對一個集合建立索引時,該集合所在的數據庫將不接受其餘讀寫操做。對數據量的集合建索引,建議使用後臺運行選項

程序配置

設定合適的MongoDB鏈接池大小 (Connections Per Host)

Java驅動的默認鏈接池大小是100。建議按照應用的實際狀況作調整。對壓力較小的應用能夠適當調小減小對應用服務器的資源佔用。

正確使用寫關注設置(Write Concern)

MongoDB的建議最小部署是一個複製集,包含3個數據節點。默認狀況下應用的寫操做(更新,插入或者刪除)在主節點上完成後就會當即返回。寫操做則經過OPLOG方式在後臺異步方式複製到其餘節點。在極端狀況下,這些寫操做可能還未在複製到從節點的時候主節點就出現宕機。這個時候發生主備節點切換,原主節點的寫操做會被回滾到文件而對應用不可見。爲防止這種狀況出現,MongoDB建議對重要的數據使用 {w: 「marjority」} 的選項。{w: 「majority」} 能夠保證數據在複製到多數節點後才返回成功結果。使用該機制能夠有效防止數據回滾的發生。 另外你可使用 (能夠和 w:」majrotiy」 結合使用) 來指定數據必須在寫入WAL日誌以後才嚮應用返回成功確認。這個會致使寫入性能有所降低,可是對於重要的數據能夠考慮使用。

正確使用讀選項設置(Read Preference)

MongoDB因爲是一個分佈式系統,一份數據會在多個節點上進行復制。從哪一個節點上讀數據,要根據應用讀數據的需求而定。如下是集中能夠配置的讀選項:

  • primary: 默認,在主節點上讀數據
  • priaryPreferred: 先從主節點上讀,若是爲成功再到任意一臺從節點上讀
  • secondary: 在從節點上讀數據(當有多臺節點的時候,隨機的使用某一臺從節點)
  • secondaryPreferred: 首先從從節點上讀,若是從節點因爲某種緣由不能提供服務,則從主節點上進行讀
  • nearest: 從距離最近的節點來讀。距離由ping操做的時間來決定。 除第一個選項以外,其餘讀選項都存在讀到的數據不是最新的可能。緣由是數據的複製是後臺異步完成的。

不要實例化多個MongoClient

MongoClient是個線程安全的類,自帶線程池。一般在一個JVM內不要實例化多個MongoClient實例,避免鏈接數過多和資源的沒必要要浪費。

對寫操做使用Retry機制

MongoDB使用複製集技術能夠實現99.999%的高可用。當一臺主節點不能寫入時,系統會自動故障轉移到另外一臺節點。轉移可能會耗時幾秒鐘,在這期間應用應該捕獲相應的Exception並執行重試操做。重試應該有backoff機制,例如,分別在1s,2s,4s,8s等時候進行重試。

避免使用太長的字段名

MongoDB 沒有表結構定義。每一個文檔的結構由每一個文檔內部的字段決定。全部字段名會在每一個文檔內重複。使用太長的字段名字會致使對內存、網絡帶寬更多的需求。(因爲壓縮技術,長字段名對硬盤上的存儲不會有太多佔用)

使用投射 (projection)來減小返回的內容

MongoDB 支持相似於SQL語句裏面的select,能夠對返回的字段進行過濾。使用Projection能夠減小返回的內容,下降網絡傳輸的量和代碼中轉化成對象所需的時間。

使用TTL來自動刪除過時的數據

不少時候咱們用MongoDB來存儲一些時效性的數據,如7天的監控數據。與其本身寫個後臺腳本按期清理過時數據,你可使用TTL索引來讓MongoDB自動刪除過時數據: db.data.ensureIndex({create_time:1}, {expireAfterSeconds: 7*24*3600})

相關文章
相關標籤/搜索