專門整理了一下我用到的監控命令和工具, 見於 : https://my.oschina.net/340StarObserver/blog/793897
當面臨性能問題的時候,首先考慮的是優化文檔結構和優化索引,其次纔是系統調優和硬件優化 在設計文檔的結構的時候,必定要很是清除你須要解決的最重要的問題是什麼 mongodb並非萬能的許願機,不少時候爲了讓你的最主要的業務流暢,你只能作出權衡 下面提出兩個原則 : 1. 文檔結構並不取決於數據的內容,而是取決於數據的訪問方式 即,不要面向數據去設計數據庫,而是面向應用去設計數據庫 2. 機關用盡時,割肉全身,善用冗餘來提升查詢效率 下面舉兩個案例 兩個案例均來自imooc上面的MongoDB2014北京大會,連接爲 http://www.imooc.com/learn/255 案例一 存儲電商的產品,每一個文檔形如 : { _id : 一個很長的字符串(全局惟一), china : { 該產品的中文描述的具體的一些屬性 }, english : { 該產品的英文描述的具體的一些屬性 }, japan : { 該產品的日文描述的具體的一些屬性 }, ...還有其餘十幾個語系 } 原始的索引是 { _id : 1 } 原始的查詢語句是 db.products.find( { _id : xxx }, { china : 1 } ) 好比這裏是一箇中國人來查詢xxx這件商品的具體信息 乍看之下這個設計確實沒問題啊,爲何會很慢 ? 通過查日誌發現,缺頁中斷太多,誒 ? 這關缺頁中斷什麼事 ? 緣由以下 : 1. 首先,mongodb在查詢的時候,無論你有沒有指定是否只返回某些字段,它都會把整個文檔查出來放到內存中 2. 只不過在mongodb客戶端(mongo-shell或者應用程序)看來,它只拿其中的china字段(固然_id是必拿的) 3. 因此,就算你只須要china字段,實際上內存中是存在着這個商品的所有信息 4. 假設總共20個語系,即內存的使用率只有 5% 5. 換句話說,內存原能夠放20個商品,結果放了5個就滿了,那當查詢其他15個商品時,缺頁中斷率不高才怪 解決方案 : 拆分文檔,拆分後的結構形如 : { _id : 原來的_id 鏈接上 語系, detail : { 該產品的在該語系下的詳細描述 } } 索引不變,如今的查詢語句變爲 db.products.find({ _id : xxxchina }) 這樣一來,mongodb查出來的就是我要用的,內存使用率提升了20倍,缺頁中斷大大減小 案例二 社交圈中的動態,涉及兩種文檔,一是用戶文檔,二是動態文檔。簡單表示以下 : 用戶文檔 : { _id : 用戶id, friends : [ 朋友1的id, 朋友2的id, ... ] } 用戶文檔的索引 { _id : 1 } 動態文檔 : { creator : 發佈者的id, time : 發佈的時間戳, content : 動態的內容 } 動態文檔的索引 { creator : 1, time : -1 } 要查出個人全部朋友最近的幾條動態,須要作聯合查詢,並且是帶in的聯合查詢,慢是正常的 解決方案 : 在每發佈一條動態的時候,順帶加上當前個人全部朋友,修改後的動態文檔的結構 : { creator : 發佈者的id, time : 發佈的時間戳, friends : [ 此時的朋友1的id, 此時的朋友2的id, ... ], content : 動態的內容 } 另外爲這個文檔增長索引 { friends : 1, time : -1 } 如今的查詢語句就不是聯合查詢了,會快不少 可是這個解決方案很奇葩是麼,搞這麼大的冗餘,多浪費磁盤啊,並且一致性的維護怎麼作? 可是,須要牢記的是,你的目標是啥?你須要解決的最大的問題是啥? 若是你的應用,容忍必定程度的不一致性,好比社交圈這種,確定是要優先照顧讀的性能,經過冗餘來提升讀性能是很常見的 你可能會有疑問 : 案例一是由於要查的信息佔整個文檔的比例過小,因此內存利用率不高致使缺頁中斷頻繁 照這個道理,案例二中增長了很大的冗餘信息,不是也會致使頻繁的缺頁中斷嗎,爲何案例二就能夠用冗餘呢 此二者不是很矛盾嗎? 我一開始也有這個疑惑,可是我要說的是,你會這麼想,是不自覺地犯了根據數據內容來設計數據庫的老病 具體分析一下 : 案例一存儲的是電商商品的信息,全部人均可能會去查詢一些熱門商品的信息 也就是說,商品信息存在熱數據,並且是被全部人均可能訪問的熱數據 因此,在這種應用場景下,爲了讓熱數據使用率更高,固然要提升內存利用率,即減小缺頁中斷的次數 既然要提升內存的利用率,就要使得用戶查的信息佔整個文檔的比例儘量大 這樣一來,才能讓固定的內存去裝的下更多數量的文檔 因此,須要把一個商品的文檔按照語系來切分紅不一樣的文檔 這樣一來,那些罕見語系的商品信息基本不會佔用內存,內存中都是熱門商品的經常使用語系的信息 案例二存儲的是不一樣用戶發表的動態信息 第一,不一樣人的動態是不一樣的。第二,並且通常查的時候是翻頁來查的 這兩個緣由就決定了這種數據是不存在熱數據的概念的 因此,絕大多數狀況,都是不可避免的缺頁中斷,都是去磁盤上查 既然沒法使用熱數據,既然不可避免地查磁盤,那就只能讓查磁盤更快這條路了 因此,使用冗餘是合適的 對比分析了這兩個案例,以及你可能出現的疑惑,相信你有了更深入的認識 再次強調 : 不要面向數據去設計數據庫,而是要面向應用去設計數據庫
和通常的sql索引很相似,不作太多闡述,這邊我只提一個以前沒有注意到的降序索引 例如消息文檔 : { myid : 個人id, who : 誰給我發的消息, time : 消息的時間戳, content : 消息的內容 } 消息文檔的索引 { myid : 1, time : -1 } 這樣的索引,能夠很快地找出與我相關的最近幾條消息,實現按時間降序分頁查詢 若是用{ myid : 1, time : 1 }索引的話 : 還須要排序,我不知道mongodb內部會不會作什麼優化,可是用降序索引確定是確保效率的 另外,當你有多種索引方案備選時,建議經過explain來看看誰更好
首先,提兩個概念 概念一. 內存映射 mongodb採用內存映射的方式來進行內存和磁盤上的數據調度 其實內存映射是操做系統自帶的,並非mongodb本身實現的 當你訪問的數據在內存中的時候,mongodb可以很快地把數據拿給你 當你訪問的數據不在內存時,會觸發缺頁中斷,告訴操做系統把xx號頁面調入內存,而後再讀內存 概念二. 工做集 工做集 = 索引 + 內存中的熱數據 如何查索引的大小? 在mongo shell中,切換到你的某個庫,經過 db.stats() 來查看 其中的 indexSize 即是該庫的全部索引佔的字節數 如何查內存中的熱數據的大小? 在mongo shell中,切換到admin庫,經過 db.serverStatus() 來查看 該命令會返回最近15分鐘內的運行狀況 若是隻須要查熱數據的大小,能夠經過 db.serverStatus()['extra_info'],個人mongo版本是3.2.4 其中的 heap_usage_bytes 即是內存中的熱數據佔的字節數 爲了讓mongodb儘可能少的發生缺頁中斷,應該讓內存大於工做集的大小 這即是選用內存大小的根據 若是單機確實沒法知足的話,就只能經過分片了 由於不少時候工做集確實很大,甚至索引的大小就超過了實際數據的大小 可是在分片的每臺主機上,選用內存的依據仍是工做集
對於不一樣的數據,採用不一樣的存儲介質sql
數據文件用SSD來存儲 mongodb絕大多數狀況下,都是隨機讀寫 相同狀況下,SSD隨機讀寫的效率是HDD的幾十倍 日誌文件用HDD來存儲 對於順序讀寫來講,SSD和HDD的效率相差不大
設置合理的預讀值mongodb
什麼是預讀值? 在請求磁盤上的數據的時候,操做系統並非只返回一個頁面,而是把接下來的好幾個頁面都調入內存中 而mongodb絕大多數是隨機讀寫,若是預讀值太大(默認是256),會致使缺頁中斷比較多 此時就須要調小預讀值,通常設置爲32比較合適 例如 : 經過 sudo blockdev --report 來查看系統各存儲設備的預讀值 經過 sudo blockdev --setra 32 /dev/sda5 來設置/dev/sda5這個分區的預讀值是32K
關閉數據庫文件的atimeshell
禁止系統對文件的訪問時間更新會有效提升文件讀取的性能 好比你的mongodb的數據在 /mydata 下面,文件系統是 ext4 能夠在 /etc/fstab 這個文件中添加一行 /dev/xvdb /mydata ext4 noatime 0 0 修改完後,從新掛載 sudo mount -o remount /mydata
我專門寫了一篇關於mongodb的分片片鍵的博客 [地址是](http://my.oschina.net/340StarObserver/blog/688232)
1. 開啓 releaseConnectionsAfterResponse 參數 db.runCommand({ setParameter : 1, releaseConnectionsAfterResponse : true })