從 mongodb 階段性技術總結 中抽取並整理了對大家有協助的十個要點:
1.mongodb 表名和欄位名統一用小寫字母mongodb 是預設區分大小寫,為了避免以前在 mysql 下遇到的大小寫敏感導致程式訪問頻頻出錯,建立規範,mongodb 的表名和欄位名都用小寫字母命名。
2.儘可能的縮短欄位名的長度
mongodb 的 schema free 導致了每筆資料都要儲存它的 key 以及屬性,這導致了這些資料的大量冗餘。開發人員也許考慮到,從易讀性出發設計的 key 名,基本都是按照字面意思去設計的,這導致 key 很長,對應的資料存放區佔用了很大的空間。所以,在你的程式裡維護一套字典即可,儘可能降低 key 的長度。譬如:static final String CONTENT = "content";static final String CONTENT_TYPE = "ctype";static final String CONTENT_LENGTH = "clen";
3.記住,mongodb 的查詢每次只能用到一個索引對於較複雜的表結構,可能會導致你頻頻使用聯合索引。但記住:1)mongodb 單表最大索引數為 64 。2)索引越多,插入或修改記錄就會導致 mongodb 越慢。寫鎖會阻塞讀請求,寫得越慢,阻塞讀請求越多、阻塞時間越長。所以,
索引越加越多的時候,你可能需要審視一下表結構設計的合理性。
4.用戶端串連數大小的設定mongodb-java-driver 的串連池,目前從觀察到的情況是應用一開啟便根據 connectionsPerHost 變數的設定,建立全部串連,然後提供給程式使用,並且一旦其中某個串連到資料庫的訪問失敗,則會清空整個串連池到這台資料庫的串連,並重建立立串連。而 mongodb 對中斷串連的垃圾清理工作則是懶惰的被動清理方式,如果驅動程式端配置的串連數過大,一旦發生重連,則會導致 mongo 伺服器端堆積大量的垃圾串連以及對應資料,導致主機資源耗盡。建議: mongodb 驅動的串連池大小的設定一般應該控制 100 左右。( 參考閱讀:PHP-FPM模式下可怕的 MongoDB-PHP-Driver 串連池無節制串連問題)
5.執行個體分離mongodb 對資料庫的訪問全部加鎖。如是查詢請求則設定共用鎖定。資料修改請求則設定全域獨佔鎖定,且是執行個體層級的獨佔鎖定。寫鎖會阻塞讀請求,如果長時間持有寫鎖,會阻塞整個執行個體的讀請求。建議:1)
不同應用不應該共用同一個執行個體,防止互相阻塞!2)如伺服器資源不足,共用同一個執行個體,
要保證讀寫特性相同,如都是讀多寫少,防止一台寫多應用阻塞讀請求。(評語:舊版本的MongoDB (pre 2.0)擁有一個全域的寫入鎖,這個問題在2.0版本中的得到了顯著的改善,並且在當前2.2版本中得到了進一步的加強。MongoDB 2.2使用資料庫層級的鎖在這個問題上邁進了一大步。
所以用 MongoDB 2.2的人可以忽略此條目。)
6.需要重點關注的 mongodb 效能指標關注主要效能指標:1)Faults:顯示 mongodb 每秒頁面故障的數量,這個是 mongodb 映射到虛擬位址空間,而不是實體記憶體。這個值如果飆高的話,可能意味著機器沒有足夠的記憶體來儲存資料和索引。2)Flushes:每秒做了多少次 fsync,顯示多少次資料被重新整理進了磁碟。3)locked:寫鎖。4)idx miss:索引未命中比例。5)qr | qw:讀寫鎖的請求隊列長度。6)conn: 當前已經建立的串連數。
7.嚴重的空間片段問題mongodb 如果資料修改很頻繁,會出現比較嚴重的空間片段問題,表現在磁碟檔案擴張與實際資料量不相符,記憶體不夠用,索引命中率低,查詢效率降低。磁碟重組,目前我們採用的版本沒有太有效方法。可以用 db.repaireDatabase() 來整理資料庫,這個過程非常的慢。如果是 master/slave 模式,則相當於執行一次主從切換,然後從建立立從庫。如果是 replSet 架構,可以停掉資料庫,然後刪除資料目錄,從新從複製組中全同步資料,這個時候要考慮 oplog 的尺寸。一個大體的步驟:1)先調用rs.freeze(1200),將每個不想讓它成為 primary 的機器讓它在 1200 秒內無法成為 primary(這步也可以不做);2)將 primary stepDown,不出意外新的 primary 會起來;3)將原 primary kill 掉;4)刪掉所有 data 資料(調用 repair 很慢,真不如幹掉重新來);5)再重啟動原 primary 的進程;6)以此迴圈完成整個複製組的全部重建。
8.串連池 WriterConcern 模式選擇有些應用配置了 WriterConcern.FSYNC_SAFE 模式;這種配置意味著用戶端在插入資料或更新資料的時候,要求 mongodb 必須將所更新的資料寫入磁碟並返回更新成功的資訊給程式。如果碰上應用程式訪問壓力大,mongodb 就會反應遲鈍,並可能會假死。針對此情況,需要評估資料的一致性需求,做出合適調整。我們一般建議關閉此選項。(評語:劉奎波的業務中心最佳化時就關閉了這個 WriterConcern.FSYNC_SAFE 模式)
9.開發時注意的細節1)
更新某條資料的時候,先查出來再更新會減小鎖的時間;2)只有真正需要的欄位才select出來;3)
只有返回很少結果的查詢才用索引,否則會載入太多資料,比沒有用索引還慢!4)屬性比較多的時候,建立分層的關係能夠提高查詢效率,否則每個記錄都要過一遍才能找到要的屬性。(評語:貌似說的是以 Array 形式儲存的 subdocument)5)skip+limit 翻頁,越往後面越慢。比較靠譜的做法是,先找出上次的id,翻頁的時候不用 skip:
last_row_id = ObjectId('....');
db.activity_stream->find({_id:{$lt: last_row_id },user_id:20 } ).sort( {_id:-1} ).limit(10);
10.關於硬體資源的選擇
虛擬機器可以很好的隔離資源,並可動態擴充。
我們建議 mongodb 的部署採用虛擬機器的方式,每個虛擬機器部署一個執行個體,使各節點分散在不同的物理機上,根據應用的前期預測,平衡虛擬機器的之間的i/o。 參考資源:1)horizonhyg,2012,Mongodb寫入安全機制--GetLastError;2)horizonhyg,2012,java串連mongo;3)55最佳實務系列:MongoDB最佳實務贈圖2枚: