MongoDB 最佳實踐


關於安全

為MongoDB集群啟用認證鑒權

MongoDB服務器在默認安裝下不啟用鑒權。這意味着每個人都可以直接連接到mongod實例並執行任意數據庫操作。

為不同用戶分配不同的角色權限

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: <30)字段應該放在組合索引的后面。

盡可能使用覆蓋索引(Covered Index)

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

建索引要在后台運行

在對一個集合創建索引時,該集合所在的數據庫將不接受其他讀寫操作。對數據量的集合建索引,建議使用后台運行選項 {background: true}

程序配置

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

Java驅動的默認連接池大小是100。建議按照應用的實際情況做調整。對壓力較小的應用可以適當調小減少對應用服務器的資源占用。

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

MongoDB的建議最小部署是一個復制集,包含3個數據節點。默認情況下應用的寫操作(更新,插入或者刪除)在主節點上完成后就會立即返回。寫操作則通過OPLOG方式在后台異步方式復制到其他節點。在極端情況下,這些寫操作可能還未在復制到從節點的時候主節點就出現宕機。這個時候發生主備節點切換,原主節點的寫操作會被回滾到文件而對應用不可見。為防止這種情況出現,MongoDB建議對重要的數據使用 {w: “marjority”} 的選項。{w: “majority”} 可以保證數據在復制到多數節點后才返回成功結果。使用該機制可以有效防止數據回滾的發生。
另外你可以使用 {j:1} (可以和 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})


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM