mongodb 性能提高的6個步驟

mongodb 性能提高的6個步驟

第一步. 作監測工做

專門整理了一下我用到的監控命令和工具,  
        見於 : 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儘可能少的發生缺頁中斷,應該讓內存大於工做集的大小  
        這即是選用內存大小的根據  
        
        若是單機確實沒法知足的話,就只能經過分片了  
        由於不少時候工做集確實很大,甚至索引的大小就超過了實際數據的大小  
        可是在分片的每臺主機上,選用內存的依據仍是工做集

第五步. 優化IO

對於不一樣的數據,採用不一樣的存儲介質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 })
相關文章
相關標籤/搜索