Mongodb高級(jí)篇-性能優(yōu)化

1、監(jiān)控

mongodb可以通過(guò)profile來(lái)監(jiān)控?cái)?shù)據(jù),進(jìn)行優(yōu)化。

查看當(dāng)前是否開(kāi)啟profile功能用命令:db.getProfilingLevel()返回level等級(jí),值為0|1|2,分別代表意思:0代表關(guān)閉,1代表記錄慢命令,2代表全部。

開(kāi)始profile功能為db.setProfilingLevel(level);

level為1的時(shí)候,慢命令默認(rèn)值為100ms,更改為db.setProfilingLevel(level,slowms)如db.setProfilingLevel(1,50)這樣就更改為50毫秒

通過(guò)db.system.profile.find() 查看當(dāng)前的監(jiān)控日志。

通過(guò)執(zhí)行db.system.profile.find({millis:{$gt:500}})能夠返回查詢(xún)時(shí)間在500毫秒以上的查詢(xún)命令。

這里值的含義是

ts:命令執(zhí)行時(shí)間
info:命令的內(nèi)容
query:代表查詢(xún)
order.order: 代表查詢(xún)的庫(kù)與集合
reslen:返回的結(jié)果集大小,byte數(shù)
nscanned:掃描記錄數(shù)量
nquery:后面是查詢(xún)條件
nreturned:返回記錄數(shù)及用時(shí)
millis:所花時(shí)間

如果發(fā)現(xiàn)時(shí)間比較長(zhǎng),那么就需要作優(yōu)化。

比如nscanned數(shù)很大,或者接近記錄總數(shù),那么可能沒(méi)有用到索引查詢(xún)。

reslen很大,有可能返回沒(méi)必要的字段。

nreturned很大,那么有可能查詢(xún)的時(shí)候沒(méi)有加限制。

mongo可以通過(guò)db.serverStatus()查看mongod的運(yùn)行狀態(tài)

2、索引

如果發(fā)現(xiàn)查詢(xún)時(shí)間相對(duì)長(zhǎng),那么就需要做優(yōu)化。首選就是為待查詢(xún)的字段建立索引,不過(guò)需要特別注意的是,索引不是萬(wàn)能靈藥。如果需要查詢(xún)超過(guò)一半的集合數(shù)據(jù),索引還不如直接遍歷來(lái)的好。

索引的原理是通過(guò)建立指定字段的B樹(shù),通過(guò)搜索B樹(shù)來(lái)查找對(duì)應(yīng)document的地址。這也就解釋了如果需要查詢(xún)超過(guò)一半的集合數(shù)據(jù),直接遍歷省去了搜索B樹(shù)的過(guò)程,效率反而會(huì)高。

關(guān)于索引,索引列顆粒越小越好,什么叫顆粒越小越好?在索引列中每個(gè)數(shù)據(jù)的重復(fù)數(shù)量稱(chēng)為顆粒,也叫作索引的基數(shù)。如果數(shù)據(jù)的顆粒過(guò)大,索引就無(wú)法發(fā)揮該有的性能。例如,我們擁有一個(gè)"age"列索引,如果在"age"列中,20歲占了50%,如果現(xiàn)在要查詢(xún)一個(gè)20歲,名叫"Tom"的人,我們則需要在表的50%的數(shù)據(jù)中查詢(xún),索引的作用大大降低。所以,我們?cè)诮⑺饕龝r(shí)要盡量將數(shù)據(jù)顆粒小的列放在索引左側(cè),以保證索引發(fā)揮最大的作用。

3、exlpain查詢(xún)執(zhí)行情況

執(zhí)行命令:

> db.order.find({ "status": 1.0, "user.uid": { $gt: 2663199.0 } }).explain()
{
    "cursor" : "BasicCursor",#游標(biāo)類(lèi)型
    "nscanned" : 2010000,#掃描數(shù)量
    "nscannedObjects" : 2010000,#掃描對(duì)象
    "n" : 337800,#返回?cái)?shù)據(jù)
    "millis" : 2838,#耗時(shí)
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : false,
    "indexOnly" : false,
    "indexBounds" : {#使用索引(這里沒(méi)有)
        
    }
}

通過(guò)這些信息就能判斷查詢(xún)時(shí)如何執(zhí)行的了

4、數(shù)據(jù)庫(kù)設(shè)計(jì)優(yōu)化

在項(xiàng)目設(shè)計(jì)階段,明確集合的用途是對(duì)性能調(diào)優(yōu)非常重要的一步。

從性能優(yōu)化的角度來(lái)看,集合的設(shè)計(jì)我們需要考慮的是集合中數(shù)據(jù)的常用操作,例如我們需要設(shè)計(jì)一個(gè)日志(log)集合,日志的查看頻率不高,但寫(xiě)入頻率卻很高,那么我們就可以得到這個(gè)集合中常用的操作是更新(增刪改)。如果我們要保存的是城市列表呢?顯而易見(jiàn),這個(gè)集合是一個(gè)查看頻率很高,但寫(xiě)入頻率很低的集合,那么常用的操作就是查詢(xún)。

對(duì)于頻繁更新和頻繁查詢(xún)的集合,我們最需要關(guān)注的重點(diǎn)是他們的范式化程度,假設(shè)現(xiàn)在我們需要存儲(chǔ)一篇圖書(shū)及其作者,在MongoDB中的關(guān)聯(lián)就可以體現(xiàn)為以下幾種形式:

1.完全分離(范式化設(shè)計(jì))

示例1:

View Code
{
     "_id" : ObjectId("5124b5d86041c7dca81917"),
     "title" : "如何使用MongoDB", 
      "author" : [ 
               ObjectId("144b5d83041c7dca84416"),
              ObjectId("144b5d83041c7dca84418"),
              ObjectId("144b5d83041c7dca84420"),
     ]
 } 

我們將作者(comment) 的id數(shù)組作為一個(gè)字段添加到了圖書(shū)中去。這樣的設(shè)計(jì)方式是在非關(guān)系型數(shù)據(jù)庫(kù)中常用的,也就是我們所說(shuō)的范式化設(shè)計(jì)。在MongoDB中我們將與主鍵沒(méi)有直接關(guān)系的圖書(shū)單獨(dú)提取到另一個(gè)集合,用存儲(chǔ)主鍵的方式進(jìn)行關(guān)聯(lián)查詢(xún)。當(dāng)我們要查詢(xún)文章和評(píng)論時(shí)需要先查詢(xún)到所需的文章,再?gòu)奈恼轮蝎@取評(píng)論id,最后用獲得的完整的文章及其評(píng)論。在這種情況下查詢(xún)性能顯然是不理想的。但當(dāng)某位作者的信息需要修改時(shí),范式化的維護(hù)優(yōu)勢(shì)就凸顯出來(lái)了,我們無(wú)需考慮此作者關(guān)聯(lián)的圖書(shū),直接進(jìn)行修改此作者的字段即可。

2.完全內(nèi)嵌(反范式化設(shè)計(jì))

示例2:

View Code
{
       "_id" : ObjectId("5124b5d86041c7dca81917"),
       "title" : "如何使用MongoDB",
       "author" : [
                {
                         "name" : "丁磊"
                         "age" : 40,
                         "nationality" : "china",
                },
                {
                         "name" : "馬云"
                         "age" : 49,
                         "nationality" : "china",
                },
                {
                         "name" : "張召忠"
                         "age" : 59,
                         "nationality" : "china",
                },
      ]
  }

在這個(gè)示例中我們將作者的字段完全嵌入到了圖書(shū)中去,在查詢(xún)的時(shí)候直接查詢(xún)圖書(shū)即可獲得所對(duì)應(yīng)作者的全部信息,但因一個(gè)作者可能有多本著作,當(dāng)修改某位作者的信息時(shí)時(shí),我們需要遍歷所有圖書(shū)以找到該作者,將其修改。

3.部分內(nèi)嵌(折中方案)

示例3:

View Code
{
       "_id" : ObjectId("5124b5d86041c7dca81917"),
       "title" : "如何使用MongoDB",
       "author" : [ 
               {
                         "_id" : ObjectId("144b5d83041c7dca84416"),
                         "name" : "丁磊"
                },
                {
                         "_id" : ObjectId("144b5d83041c7dca84418"),
                         "name" : "馬云"
                },
                {
                         "_id" : ObjectId("144b5d83041c7dca84420"),
                         "name" : "張召忠"
                },
      ]
  }

這次我們將作者字段中的最常用的一部分提取出來(lái)。當(dāng)我們只需要獲得圖書(shū)和作者名時(shí),無(wú)需再次進(jìn)入作者集合進(jìn)行查詢(xún),僅在圖書(shū)集合查詢(xún)即可獲得。

這種方式是一種相對(duì)折中的方式,既保證了查詢(xún)效率,也保證的更新效率。但這樣的方式顯然要比前兩種較難以掌握,難點(diǎn)在于需要與實(shí)際業(yè)務(wù)進(jìn)行結(jié)合來(lái)尋找合適的提取字段。如同示例3所述,名字顯然不是一個(gè)經(jīng)常修改的字段,這樣的字段如果提取出來(lái)是沒(méi)問(wèn)題的,但如果提取出來(lái)的字段是一個(gè)經(jīng)常修改的字段(比如age)的話,我們依舊在更新這個(gè)字段時(shí)需要大范圍的尋找并依此進(jìn)行更新。

在上面三個(gè)示例中,第一個(gè)示例的更新效率是最高的,但查詢(xún)效率是最低的,而第二個(gè)示例的查詢(xún)效率最高,但更新效率最低。所以在實(shí)際的工作中我們需要根據(jù)自己實(shí)際的需要來(lái)設(shè)計(jì)表中的字段,以獲得最高的效率。

5、其他方法

熱數(shù)據(jù)法

可能你的數(shù)據(jù)集非常大,但是這并不那么重要,重要的是你的熱數(shù)據(jù)集有多大,你經(jīng)常訪問(wèn)的數(shù)據(jù)有多大(包括經(jīng)常訪問(wèn)的數(shù)據(jù)和所有索引數(shù)據(jù))。使用MongoDB,你最好保證你的熱數(shù)據(jù)在你機(jī)器的內(nèi)存大小之下,保證內(nèi)存能容納所有熱數(shù)據(jù)。

文件系統(tǒng)法

MongoDB的數(shù)據(jù)文件是采用的預(yù)分配模式,并且在Replication里面,Master和Replica Sets的非Arbiter節(jié)點(diǎn)都是會(huì)預(yù)先創(chuàng)建足夠的空文件用以存儲(chǔ)操作日志。這些文件分配操作在一些文件系統(tǒng)上可能會(huì)非常慢,導(dǎo)致進(jìn)程被Block。所以我們應(yīng)該選擇那些空間分配快速的文件系統(tǒng)。這里的結(jié)論是盡量不要用ext3,用ext4或者xfs。

硬件法

這里的選擇包括了對(duì)磁盤(pán)RAID的選擇,也包括了磁盤(pán)與SSD的對(duì)比選擇。

其他

如果數(shù)據(jù)文件大于系統(tǒng)內(nèi)存,查詢(xún)速度會(huì)下降幾個(gè)數(shù)量級(jí),因?yàn)閙ongodb是內(nèi)存數(shù)據(jù)庫(kù)。我以前測(cè)試過(guò),1000萬(wàn)數(shù)據(jù)的時(shí)候沒(méi)有索引情況下查詢(xún)可能會(huì)幾秒鐘甚至更久。

這種情況,你最好給經(jīng)常查詢(xún)的項(xiàng)創(chuàng)建索引,有索引以后查詢(xún)速度會(huì)非常非常非常的快。

另外一點(diǎn)是數(shù)據(jù)索引如果大于內(nèi)存,速度也會(huì)下降很多。而且對(duì)于多條件查詢(xún),如果你查詢(xún)的順學(xué)和索引順序不同,也不能使用索引。這個(gè)要慢慢摸索

如果你使用了replica set,這個(gè)會(huì)影響寫(xiě)入速度的,三個(gè)replica set,速度會(huì)降低到三分之一。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容