生產(chǎn)環(huán)境最佳實(shí)踐 1.linux 系統(tǒng): 1】關(guān)閉文件系統(tǒng)/分區(qū)的atime 選項(xiàng) Vi /etc/fstab 在對(duì)應(yīng)的分區(qū)項(xiàng)后面添加noatime ,nodiratime LABEL=/1 / ext3 defaults 1 1 LABEL=/data1 /data ext4 defaults,noatime,nodiratime 1 2 2】設(shè)置文件句柄4k+,目前該配置已經(jīng)集成到啟動(dòng)腳本中。 Vi /etc/security/limit.conf * soft nproc 65536 * hard nproc 65536 * soft nofile 65536 * hard nofile 65536 3】不要使用large vm page (不要使用大內(nèi)存頁(yè)選項(xiàng)) Linux 大內(nèi)存頁(yè)參考:http:///155/krishnakumar.html 4】用dmesg 查看主機(jī)的信息。 2.linux 文件系統(tǒng)的選擇: Mongodb 采用預(yù)分配的大文件來(lái)存儲(chǔ)數(shù)據(jù),我們推薦 1】ext4 2】xfs 3.內(nèi)核版本: 網(wǎng)絡(luò)上對(duì)2.6.33-31 以及2.6.32 的表現(xiàn)持懷疑度, 而強(qiáng)力推薦2.6.36 .ext4文件系統(tǒng)數(shù)據(jù)損失bug就影響著多個(gè)穩(wěn)定版內(nèi)核。ext4用戶應(yīng)避免使用Linux 3.4.14、3.4.15、3.5.7、3.6.2和3.6.3。 4.線程堆棧的尺寸 默認(rèn)的線程堆棧尺寸為10m ,調(diào)整為1m ,已經(jīng)集成在啟動(dòng)腳本中。 項(xiàng)目過(guò)程中的總結(jié)與建議 1.大小寫(xiě)問(wèn)題 mongodb 是默認(rèn)區(qū)分大小寫(xiě)的,但是這會(huì)不會(huì)衍生出跟mysql 一樣的問(wèn)題?(mysql 區(qū) 分大小寫(xiě),導(dǎo)致windows 與linux 下的表名,字段名不一致)。 如果無(wú)特別用途,建議表名,字段名全部用小寫(xiě)字母。 2.盡可能的縮短字段名的長(zhǎng)度 mongodb 的schema free 導(dǎo)致了每筆數(shù)據(jù)都要存儲(chǔ)他的key 以及屬性,這導(dǎo)致了這些數(shù) 據(jù)的大量冗余。開(kāi)發(fā)同事也許考慮到,從易讀性出發(fā)設(shè)計(jì)的key 基本比較長(zhǎng),基本都是按 照起字面意思去設(shè)計(jì)的。這導(dǎo)致key 很長(zhǎng)。對(duì)應(yīng)的數(shù)據(jù)存儲(chǔ)占用了很大的空間。 必要的時(shí)候,可以考慮建立一個(gè)key 與實(shí)際意義的map 表,盡量降低key 的長(zhǎng)度。 示例定義: // 基本信息 static string _ID = "_id"; static string STATUS_CODE = "sc"; // 緩沖 static string DATE = "date"; static string MAX_AGE = "age"; // 內(nèi)容 static string CONTENT = "content"; static string CONTENT_TYPE = "ctype"; static string CONTENT_LENGTH = "clen"; static string ZIP = "zip"; 3. mongodb 單表最大索引數(shù)為64 無(wú)索引排序的最大數(shù)據(jù)量為4M, 超過(guò)則報(bào)錯(cuò)退出。 建議where 條件盡量落在索引字段上,排序字段需要建立索引,索引的使用原則與oracle mysql 一致,盡量降低索引數(shù)量,索引長(zhǎng)度。 mongodb 的查詢每次只能用到一個(gè)索引,對(duì)數(shù)據(jù)的查詢不會(huì)“并發(fā)”執(zhí)行 例如: db.tab.find({'id'=1,'name'=2}) 如果‘id’,‘name' 列上分別有索引 對(duì)查詢效率提升意義不大,如果索引為('id','name') 則大幅提升效率。 4.mongodb 添加字段 如果添加字段且?guī)в衐efault 值,需要全部數(shù)據(jù)都要修改,這也是設(shè)計(jì)階段需要考慮的 事情,這個(gè)問(wèn)題的另外一種解法是應(yīng)用代碼里做一次判斷。 5.測(cè)試過(guò)程的密碼問(wèn)題 對(duì)于用作數(shù)據(jù)庫(kù)使用的Mongodb,在代碼測(cè)試階段都應(yīng)加上密碼驗(yàn)證,目前上線階段基 本都會(huì)在密碼驗(yàn)證方面出現(xiàn)問(wèn)題(做緩存使用的可以不做密碼驗(yàn)證)。 6.數(shù)據(jù)源連接方式 使用連接池模式,盡量減少認(rèn)證帶來(lái)的性能額外消耗 建議采用標(biāo)準(zhǔn)的uri 連接方式: mongodb://user:passwd@host:port,host:port/db 7.Mongodb日志量 正常情況下不需要開(kāi)啟-v 日志選項(xiàng)。 Mongodb 的-v 日志適合在開(kāi)發(fā)環(huán)境的調(diào)試線上部署不建議采用這個(gè)參數(shù),目前線上 部署的情況,-v 日志一天也會(huì)有幾個(gè)G 的日志量,去掉這個(gè)參數(shù),跟數(shù)據(jù)查詢相關(guān)的操作 就不會(huì)記日志了,數(shù)據(jù)庫(kù)的內(nèi)部的重要操作還是會(huì)寫(xiě)日志的。 8.連接數(shù)大小的設(shè)置 Mongodb 驅(qū)動(dòng)程序采用的連接池的方式連接到數(shù)據(jù)庫(kù),目前從觀察到的情況是應(yīng)用一 開(kāi)啟便根據(jù)變量的設(shè)置,建立全部連接,然后提供給程序使用,并且一旦其中某個(gè)連接 到數(shù)據(jù)庫(kù)的訪問(wèn)失敗,則會(huì)清空整個(gè)連接池到這臺(tái)數(shù)據(jù)庫(kù)的連接,并重新建立連接。 而mongodb 對(duì)中斷連接的垃圾清理工作則是懶惰的被動(dòng)清理方式,如果驅(qū)動(dòng)程序端配 置的連接數(shù)過(guò)大,一旦發(fā)生重連,則會(huì)導(dǎo)致mongo 端堆積大量的垃圾連接數(shù)據(jù),導(dǎo)致 主機(jī)資源耗盡。 建議: mongodb 驅(qū)動(dòng)的連接池大小的設(shè)置一般應(yīng)該控制100 以下,一般情況30-50 足 夠支撐應(yīng)用訪問(wèn)。 9.鎖的問(wèn)題 Mongodb 對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)全部加鎖,如果是查詢請(qǐng)求則設(shè)置共享鎖,數(shù)據(jù)修改請(qǐng)求, 則設(shè)置全局排他鎖,并且是實(shí)例級(jí)別的排他鎖。并且寫(xiě)鎖會(huì)阻塞讀請(qǐng)求,如果長(zhǎng)時(shí)間持有 寫(xiě)鎖,會(huì)阻塞整個(gè)實(shí)例的讀請(qǐng)求。 部署建議: 1】一般情況下,建議不同的應(yīng)用不要合用一套示例。 2】如果資源不滿足,需要合用,應(yīng)該具有相同屬性的應(yīng)用合用一套實(shí)例。 例如合同mongo 的應(yīng)用都是讀多寫(xiě)少,防止一臺(tái)寫(xiě)多應(yīng)用阻塞讀請(qǐng)求。 10.關(guān)于map/reduce問(wèn)題 mongodb 對(duì)map/reduce 的支持是單線程的,我們不建議在前臺(tái)使用該功能, group by 是通過(guò)map/reduce 實(shí)現(xiàn)的,開(kāi)發(fā)過(guò)程中,要慎用。 11.安全問(wèn)題 1】Mongodb 運(yùn)行在mongodb 用戶之上,并禁止mongodb 用戶登錄 2】使用Mongodb 自帶的認(rèn)證方法(adduser、auth)限制用戶訪問(wèn)行為 3】將Mongodb 置于內(nèi)網(wǎng)環(huán)境中 4】Mongodb 必須暴露在外網(wǎng)環(huán)境中的時(shí)候,使用IPTABLES 等網(wǎng)絡(luò)層技術(shù)進(jìn)行防護(hù) 5】網(wǎng)絡(luò)層面內(nèi)容為明文傳輸,可以考慮存儲(chǔ)加密文檔,應(yīng)用端,加解密。 12.性能監(jiān)控 Mongodb 自帶有性能數(shù)據(jù)收集系統(tǒng) Mongostat 實(shí)時(shí)采集數(shù)據(jù)庫(kù)的多項(xiàng)指標(biāo),提供http console 端口號(hào)為應(yīng)用端口號(hào)+1000。 關(guān)注的主要性能指標(biāo): 1】Faults:顯示Mongodb 每秒頁(yè)面故障的數(shù)量,這個(gè)是mongoDB 映射到虛擬地址空間, 而不是物理內(nèi)存,這個(gè)值如果飆高的話,可能意味著機(jī)器沒(méi)有足夠的內(nèi)存來(lái) 存儲(chǔ)數(shù)據(jù)和索引。 2】Flushes:每秒做了多少次fsync,顯示多少次數(shù)據(jù)被刷新進(jìn)了磁盤(pán) 3】locked:寫(xiě)鎖 4】idx miss:索引未命中比例 5】qr | qw:讀寫(xiě)鎖的請(qǐng)求隊(duì)列長(zhǎng)度。 6】conn: 當(dāng)前已經(jīng)建立的連接數(shù)。 其他命令: Db.stat() db.serverStatuse() Db.collection.stats() 13.碎片問(wèn)題 Mongodb 數(shù)據(jù)庫(kù)如果數(shù)據(jù)修改很頻繁,會(huì)出現(xiàn)比較嚴(yán)重的空間碎片問(wèn)題,表現(xiàn)在磁盤(pán) 文件擴(kuò)張與實(shí)際數(shù)據(jù)量不相符,內(nèi)存不夠用,索引命中率低,查詢效率降低。 碎片整理,目前我們采用的版本沒(méi)有太有效的方法。 可以用db.repaireDatabase() 來(lái)整理數(shù)據(jù)庫(kù),這個(gè)過(guò)程非常的慢 如果是Master-slave 模式則相當(dāng)于執(zhí)行一次主從切換,然后從新建立從庫(kù)。 如果是replSet 架構(gòu)可以停掉數(shù)據(jù)庫(kù),然后刪除數(shù)據(jù)目錄,從新從復(fù)制復(fù)制組中全同步數(shù)據(jù), 這個(gè)時(shí)候要考慮oplog 的尺寸。 一個(gè)大體的步驟: 1.】先調(diào)用rs.freeze(1200),將每個(gè)不想讓它成為primary 的機(jī)器讓它在1200 秒內(nèi)無(wú)法成為 primary(這步也可以不做) 2. 】將primary stepDown,不出意外新的primary 會(huì)起來(lái). 3. 】將原primary kill 掉. 4. 】刪掉所有data 數(shù)據(jù)(調(diào)用repair 很慢,真不如干掉重新來(lái)) 5. 】再重啟動(dòng)原primary 的進(jìn)程 6. 】以此循環(huán)完成整個(gè)復(fù)制組的全部重建。 14.系統(tǒng)備份: Mongodb 目前不支持在線備份,只能離線備份。 我們采用的架構(gòu)為replSet 和Master-slave . 基于我們目前的架構(gòu)以及數(shù)據(jù)一致性要求,我們沒(méi)有安排相關(guān)的備份系統(tǒng)。 15.應(yīng)用代碼中Mongodb連接問(wèn)題 在有些應(yīng)用在使用Mongodb 過(guò)程中會(huì)存在以下兩個(gè)小問(wèn)題: 1. 在應(yīng)用啟動(dòng)過(guò)程中,應(yīng)用存在要求連接池中所有的連接都建立成功才讓?xiě)?yīng)用正 常啟動(dòng),這種做法不可取,因?yàn)榇嬖诰W(wǎng)絡(luò)問(wèn)題、Mongodb 拒絕連接或Mongodb 假死情況,如 果沒(méi)加外部try catch 做防護(hù),則不斷重啟也不能正常啟動(dòng)端口。 2.有些應(yīng)用在使用Mongodb 中連接池配置了safe=true,w=1;這種配置意味著客戶端在 插入數(shù)據(jù)或更新數(shù)據(jù)的時(shí)候,要求mongodb 必須將所更新的數(shù)據(jù)寫(xiě)入磁盤(pán)并返回更新成功 的信息給程序。如果碰上應(yīng)用程序訪問(wèn)壓力大,mongodb 就會(huì)反應(yīng)遲鈍,并會(huì)發(fā)生假死可能, 針對(duì)此情況,需要評(píng)估數(shù)據(jù)的一致性需求,做出合適調(diào)整。我們一般建議關(guān)閉此選項(xiàng)。 16.補(bǔ)充開(kāi)發(fā)方面的一些問(wèn)題 1】skip+limit翻頁(yè),越往后面越慢,有資料說(shuō)用數(shù)組元素的分頁(yè)可以解決,目前還沒(méi) 試過(guò),比較靠譜的做法是,先找出上次的id,翻頁(yè)的時(shí)候不用skip: last_row_id = ObjectId(‘....’); db.activity_stream->find({_id:{$lt: last_row_id }, user_id:20 } ).sort( {_id:-1} ).limit(10); 2】.只有真正需要的字段才select出來(lái) 3】.更新的某條數(shù)據(jù)的時(shí)候,先查出來(lái)再更新會(huì)減小鎖的時(shí)間 4】.只有返回很少結(jié)果的查詢才用索引,否則會(huì)加載太多數(shù)據(jù),比沒(méi)有用索引還慢 5】.屬性比較多的時(shí)候,建立分層的關(guān)系能夠提高查詢效率,否則每個(gè)記錄都要過(guò)一遍 才能找到要的屬性 1.mongodb 表名和字段名統(tǒng)一用小寫(xiě)字母
mongodb 是默認(rèn)區(qū)分大小寫(xiě)的,為了避免以前在 mysql 下遇到的大小寫(xiě)敏感導(dǎo)致程序訪問(wèn)頻頻出錯(cuò),
建立規(guī)范,mongodb 的表名和字段名都用小寫(xiě)字母命名。
2.盡可能的縮短字段名的長(zhǎng)度
mongodb 的 schema free 導(dǎo)致了每筆數(shù)據(jù)都要存儲(chǔ)它的 key 以及屬性,這導(dǎo)致了這些數(shù)據(jù)的大量冗余。
開(kāi)發(fā)人員也許考慮到,從易讀性出發(fā)設(shè)計(jì)的 key 名,基本都是按照字面意思去設(shè)計(jì)的,這導(dǎo)致 key 很長(zhǎng),對(duì)應(yīng)的數(shù)據(jù)存儲(chǔ)占用了很大的空間。
所以,在你的程序里維護(hù)一套字典即可,盡可能降低 key 的長(zhǎng)度。
譬如:
static final String CONTENT = "content";
static final String CONTENT_TYPE = "ctype";
static final String CONTENT_LENGTH = "clen";
3.記住,mongodb 的查詢每次只能用到一個(gè)索引
對(duì)于較復(fù)雜的表結(jié)構(gòu),可能會(huì)導(dǎo)致你頻頻使用聯(lián)合索引。
但記?。?/div>
1)mongodb 單表最大索引數(shù)為 64 。
2)索引越多,插入或修改記錄就會(huì)導(dǎo)致 mongodb 越慢。寫(xiě)鎖會(huì)阻塞讀請(qǐng)求,寫(xiě)得越慢,阻塞讀請(qǐng)求越多、阻塞時(shí)間越長(zhǎng)。
所以,索引越加越多的時(shí)候,你可能需要審視一下表結(jié)構(gòu)設(shè)計(jì)的合理性。
4.客戶端連接數(shù)大小的設(shè)置
mongodb-java-driver 的連接池,目前從觀察到的情況是應(yīng)用一開(kāi)啟便根據(jù) connectionsPerHost
變量的設(shè)置,建立全部連接,然后提供給程序使用,并且一旦其中某個(gè)連接到數(shù)據(jù)庫(kù)的訪問(wèn)失敗,則會(huì)清空整個(gè)連接池到這臺(tái)數(shù)據(jù)庫(kù)的連接,并重新建立連接。
而 mongodb 對(duì)中斷連接的垃圾清理工作則是懶惰的被動(dòng)清理方式,如果驅(qū)動(dòng)程序端配置的連接數(shù)過(guò)大,一旦發(fā)生重連,則會(huì)導(dǎo)致 mongo 服務(wù)器端堆積大量的垃圾連接以及對(duì)應(yīng)數(shù)據(jù),導(dǎo)致主機(jī)資源耗盡。
建議: mongodb 驅(qū)動(dòng)的連接池大小的設(shè)置一般應(yīng)該控制 100 左右。
5.實(shí)例分離
mongodb 對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)全部加鎖。
如是查詢請(qǐng)求則設(shè)置共享鎖。
數(shù)據(jù)修改請(qǐng)求則設(shè)置全局排他鎖,且是實(shí)例級(jí)別的排他鎖。
寫(xiě)鎖會(huì)阻塞讀請(qǐng)求,如果長(zhǎng)時(shí)間持有寫(xiě)鎖,會(huì)阻塞整個(gè)實(shí)例的讀請(qǐng)求。
建議:
1)不同應(yīng)用不應(yīng)該共用同一個(gè)實(shí)例,防止互相阻塞!
2)如服務(wù)器資源不足,共用同一個(gè)實(shí)例,要保證讀寫(xiě)特性相同,如都是讀多寫(xiě)少,防止一臺(tái)寫(xiě)多應(yīng)用阻塞讀請(qǐng)求。
(評(píng)語(yǔ):舊版本的MongoDB (pre 2.0)擁有一個(gè)全局的寫(xiě)入鎖,這個(gè)問(wèn)題在2.0版本中的得到了顯著的改善,并且在當(dāng)前2.2版本中得到了進(jìn)一步的加強(qiáng)。MongoDB 2.2使用數(shù)據(jù)庫(kù)級(jí)別的鎖在這個(gè)問(wèn)題上邁進(jìn)了一大步。所以用 MongoDB 2.2的人可以忽略此條目。)
6.需要重點(diǎn)關(guān)注的 mongodb 性能指標(biāo)
關(guān)注主要性能指標(biāo):
1)Faults:顯示 mongodb 每秒頁(yè)面故障的數(shù)量,這個(gè)是 mongodb 映射到虛擬地址空間,而不是物理內(nèi)存。這個(gè)值如果飆高的話,可能意味著機(jī)器沒(méi)有足夠的內(nèi)存來(lái)存儲(chǔ)數(shù)據(jù)和索引。
2)Flushes:每秒做了多少次 fsync,顯示多少次數(shù)據(jù)被刷新進(jìn)了磁盤(pán)。
3)locked:寫(xiě)鎖。
4)idx miss:索引未命中比例。
5)qr | qw:讀寫(xiě)鎖的請(qǐng)求隊(duì)列長(zhǎng)度。
6)conn: 當(dāng)前已經(jīng)建立的連接數(shù)。
7.嚴(yán)重的空間碎片問(wèn)題
mongodb 如果數(shù)據(jù)修改很頻繁,會(huì)出現(xiàn)比較嚴(yán)重的空間碎片問(wèn)題,表現(xiàn)在磁盤(pán)文件擴(kuò)張與實(shí)際數(shù)據(jù)量不相符,內(nèi)存不夠用,索引命中率低,查詢效率降低。
碎片整理,目前我們采用的版本沒(méi)有太有效的方法。
可以用 db.repaireDatabase() 來(lái)整理數(shù)據(jù)庫(kù),這個(gè)過(guò)程非常的慢。
如果是 master/slave 模式,則相當(dāng)于執(zhí)行一次主從切換,然后從新建立從庫(kù)。
如果是 replSet 架構(gòu),可以停掉數(shù)據(jù)庫(kù),然后刪除數(shù)據(jù)目錄,從新從復(fù)制組中全同步數(shù)據(jù),這個(gè)時(shí)候要考慮 oplog 的尺寸。
一個(gè)大體的步驟:
1)先調(diào)用rs.freeze(1200),將每個(gè)不想讓它成為 primary 的機(jī)器讓它在 1200 秒內(nèi)無(wú)法成為 primary(這步也可以不做);
2)將 primary stepDown,不出意外新的 primary 會(huì)起來(lái);
3)將原 primary kill 掉;
4)刪掉所有 data 數(shù)據(jù)(調(diào)用 repair 很慢,真不如干掉重新來(lái));
5)再重啟動(dòng)原 primary 的進(jìn)程;
6)以此循環(huán)完成整個(gè)復(fù)制組的全部重建。
8.連接池 WriterConcern 模式選擇
有些應(yīng)用配置了 WriterConcern.FSYNC_SAFE 模式;這種配置意味著客戶端在插入數(shù)據(jù)或更新數(shù)據(jù)的時(shí)候,要求 mongodb 必須將所更新的數(shù)據(jù)寫(xiě)入磁盤(pán)并返回更新成功的信息給程序。
如果碰上應(yīng)用程序訪問(wèn)壓力大,mongodb 就會(huì)反應(yīng)遲鈍,并可能會(huì)假死。
針對(duì)此情況,需要評(píng)估數(shù)據(jù)的一致性需求,做出合適調(diào)整。
我們一般建議關(guān)閉此選項(xiàng)。
(評(píng)語(yǔ):劉奎波的業(yè)務(wù)中心優(yōu)化時(shí)就關(guān)閉了這個(gè) WriterConcern.FSYNC_SAFE 模式)
9.開(kāi)發(fā)時(shí)注意的細(xì)節(jié)
1)更新某條數(shù)據(jù)的時(shí)候,先查出來(lái)再更新會(huì)減小鎖的時(shí)間;
2)只有真正需要的字段才select出來(lái);
3)只有返回很少結(jié)果的查詢才用索引,否則會(huì)加載太多數(shù)據(jù),比沒(méi)有用索引還慢!
4)屬性比較多的時(shí)候,建立分層的關(guān)系能夠提高查詢效率,否則每個(gè)記錄都要過(guò)一遍才能找到要的屬性。(評(píng)語(yǔ):貌似說(shuō)的是以 Array 形式存儲(chǔ)的 subdocument)
5)skip+limit 翻頁(yè),越往后面越慢。比較靠譜的做法是,先找出上次的id,翻頁(yè)的時(shí)候不用 skip:
last_row_id = ObjectId('....'); 10.關(guān)于硬件資源的選擇 虛擬機(jī)可以很好的隔離資源,并可動(dòng)態(tài)的擴(kuò)展。 我們建議 mongodb 的部署采用虛擬機(jī)的方式,每個(gè)虛擬機(jī)部署一個(gè)實(shí)例,使各節(jié)點(diǎn)分散在不同的物理機(jī)上,根據(jù)應(yīng)用的前期預(yù)測(cè),平衡虛擬機(jī)的之間的i/o。 |
|
來(lái)自: 博雅書(shū)屋lhs > 《MongoDB》