mongodb 階段性技術總結

babyyellow發表於2011-11-02
聯合開發,運維,對mongodb 做了一個技術總結。 其中一些關於公司的資料,就擦去了,各位見諒。

生產環境最佳實踐

1.linux 系統:
1】關閉檔案系統/分割槽的atime 選項
Vi /etc/fstab
在對應的分割槽項後面新增noatime ,nodiratime
LABEL=/1 / ext3 defaults 1 1
LABEL=/data1 /data ext4 defaults,noatime,nodiratime 1 2
2】設定檔案控制程式碼4k+,目前該配置已經整合到啟動指令碼中。
Vi /etc/security/limit.conf
* soft nproc 65536
* hard nproc 65536
* soft nofile 65536
* hard nofile 65536
3】不要使用large vm page (不要使用大記憶體頁選項)
Linux 大記憶體頁參考:
4】用dmesg 檢視主機的資訊。
2.linux 檔案系統的選擇:
Mongodb 採用預分配的大檔案來儲存資料,我們推薦
1】ext4
2】xfs
3.核心版本:
網路上對2.6.33-31 以及2.6.32 的表現持懷疑度, 而強力推薦2.6.36
4.執行緒堆疊的尺寸
預設的執行緒堆疊尺寸為10m ,調整為1m ,已經整合在啟動指令碼中。

專案過程中的總結與建議

1.大小寫問題
mongodb 是預設區分大小寫的,但是這會不會衍生出跟mysql 一樣的問題?(mysql 區
分大小寫,導致windows 與linux 下的表名,欄位名不一致)。
如果無特別用途,建議表名,欄位名全部用小寫字母。

2.儘可能的縮短欄位名的長度
mongodb 的schema free 導致了每筆資料都要儲存他的key 以及屬性,這導致了這些數
據的大量冗餘。開發同事也許考慮到,從易讀性出發設計的key 基本比較長,基本都是按
照起字面意思去設計的。這導致key 很長。對應的資料儲存佔用了很大的空間。
必要的時候,可以考慮建立一個key 與實際意義的map 表,儘量降低key 的長度。
示例定義:
// 基本資訊
static final String _ID = "_id";
static final String STATUS_CODE = "sc";
// 緩衝
static final String DATE = "date";
static final String MAX_AGE = "age";
// 內容
static final String CONTENT = "content";
static final String CONTENT_TYPE = "ctype";
static final String CONTENT_LENGTH = "clen";
static final String ZIP = "zip";

3. mongodb 單表最大索引數為64
無索引排序的最大資料量為4M, 超過則報錯退出。
建議where 條件儘量落在索引欄位上,排序欄位需要建立索引,索引的使用原則與oracle
mysql 一致,儘量降低索引數量,索引長度。
mongodb 的查詢每次只能用到一個索引,對資料的查詢不會“併發”執行
例如: db.tab.find({'id'=1,'name'=2}) 如果‘id’,‘name' 列上分別有索引
對查詢效率提升意義不大,如果索引為('id','name') 則大幅提升效率。

4.mongodb 新增欄位
如果新增欄位且帶有default 值,需要全部資料都要修改,這也是設計階段需要考慮的
事情,這個問題的另外一種解法是應用程式碼裡做一次判斷。

5.測試過程的密碼問題
對於用作資料庫使用的Mongodb,在程式碼測試階段都應加上密碼驗證,目前上線階段基
本都會在密碼驗證方面出現問題(做快取使用的可以不做密碼驗證)。

6.資料來源連線方式
使用連線池模式,儘量減少認證帶來的效能額外消耗
建議採用標準的uri 連線方式: mongodb://user:passwd@host:port,host:port/db

7.Mongodb日誌量
正常情況下不需要開啟-v 日誌選項。
Mongodb 的-v 日誌適合在開發環境的除錯線上部署不建議採用這個引數,目前線上
部署的情況,-v 日誌一天也會有幾個G 的日誌量,去掉這個引數,跟資料查詢相關的操作
就不會記日誌了,資料庫的內部的重要操作還是會寫日誌的。

8.連線數大小的設定
Mongodb 驅動程式採用的連線池的方式連線到資料庫,目前從觀察到的情況是應用一
開啟便根據變數的設定,建立全部連線,然後提供給程式使用,並且一旦其中某個連線
到資料庫的訪問失敗,則會清空整個連線池到這臺資料庫的連線,並重新建立連線。
而mongodb 對中斷連線的垃圾清理工作則是懶惰的被動清理方式,如果驅動程式端配
置的連線數過大,一旦發生重連,則會導致mongo 端堆積大量的垃圾連線資料,導致
主機資源耗盡。
建議: mongodb 驅動的連線池大小的設定一般應該控制100 以下,一般情況30-50 足
夠支撐應用訪問。

9.鎖的問題
Mongodb 對資料庫的訪問全部加鎖,如果是查詢請求則設定共享鎖,資料修改請求,
則設定全域性排他鎖,並且是例項級別的排他鎖。並且寫鎖會阻塞讀請求,如果長時間持有
寫鎖,會阻塞整個例項的讀請求。
部署建議:
1】一般情況下,建議不同的應用不要合用一套示例。
2】如果資源不滿足,需要合用,應該具有相同屬性的應用合用一套例項。
例如合同mongo 的應用都是讀多寫少,防止一臺寫多應用阻塞讀請求。

10.關於map/reduce問題
mongodb 對map/reduce 的支援是單執行緒的,我們不建議在前臺使用該功能, group by
是透過map/reduce 實現的,開發過程中,要慎用。

11.安全問題
1】Mongodb 執行在mongodb 使用者之上,並禁止mongodb 使用者登入
2】使用Mongodb 自帶的認證方法(adduser、auth)限制使用者訪問行為
3】將Mongodb 置於內網環境中
4】Mongodb 必須暴露在外網環境中的時候,使用IPTABLES 等網路層技術進行防護
5】網路層面內容為明文傳輸,可以考慮儲存加密文件,應用端,加解密。

12.效能監控
Mongodb 自帶有效能資料收集系統
Mongostat 實時採集資料庫的多項指標,提供http console 埠號為應用埠號+1000。
關注的主要效能指標:
1】Faults:顯示Mongodb 每秒頁面故障的數量,這個是mongoDB 對映到虛擬地址空間,
而不是實體記憶體,這個值如果飆高的話,可能意味著機器沒有足夠的記憶體來
儲存資料和索引。
2】Flushes:每秒做了多少次fsync,顯示多少次資料被重新整理進了磁碟
3】locked:寫鎖
4】idx miss:索引未命中比例
5】qr | qw:讀寫鎖的請求佇列長度。
6】conn: 當前已經建立的連線數。
其他命令:
Db.stat()
db.serverStatuse()
Db.collection.stats()

13.碎片問題
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. 】以此迴圈完成整個複製組的全部重建。

14.系統備份:
Mongodb 目前不支援線上備份,只能離線備份。
我們採用的架構為replSet 和Master-slave .
基於我們目前的架構以及資料一致性要求,我們沒有安排相關的備份系統。

15.應用程式碼中Mongodb連線問題
在有些應用在使用Mongodb 過程中會存在以下兩個小問題:
1. 在應用啟動過程中,應用存在要求連線池中所有的連線都建立成功才讓應用正
常啟動,這種做法不可取,因為存在網路問題、Mongodb 拒絕連線或Mongodb 假死情況,如
果沒加外部try catch 做防護,則Resin 不斷重啟也不能正常啟動埠。
2.有些應用在使用Mongodb 中連線池配置了safe=true,w=1;這種配置意味著客戶端在
插入資料或更新資料的時候,要求mongodb 必須將所更新的資料寫入磁碟並返回更新成功
的資訊給程式。如果碰上應用程式訪問壓力大,mongodb 就會反應遲鈍,並會發生假死可能,
針對此情況,需要評估資料的一致性需求,做出合適調整。我們一般建議關閉此選項。

16.補充開發方面的一些問題
1】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);
2】.只有真正需要的欄位才select出來
3】.更新的某條資料的時候,先查出來再更新會減小鎖的時間
4】.只有返回很少結果的查詢才用索引,否則會載入太多資料,比沒有用索引還慢
5】.屬性比較多的時候,建立分層的關係能夠提高查詢效率,否則每個記錄都要過一遍
才能找到要的屬性

17.關於硬體資源的選擇:
虛擬機器可以很好的隔離資源,並可動態的擴充套件。
我們建議mongodb 的部署採用虛擬機器的方式,每個虛擬機器部署一個例項,使各節點分
散在不同的物理機上,根據應用的前期預測,平衡虛擬機器的之間的i/o。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/133735/viewspace-710117/,如需轉載,請註明出處,否則將追究法律責任。

相關文章