亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

MongoDB復(fù)合索引詳解

qieangel2013 / 2595人閱讀

摘要:摘要對于的多鍵查詢,創(chuàng)建復(fù)合索引可以有效提高性能。不妨通過一個(gè)簡單的示例理解復(fù)合索引。但是,使用的是與的復(fù)合索引即根據(jù)索引去查詢文檔,不需要過濾??梢酝茰y,應(yīng)該是索引的問題導(dǎo)致的。

摘要: 對于MongoDB的多鍵查詢,創(chuàng)建復(fù)合索引可以有效提高性能。

什么是復(fù)合索引?

復(fù)合索引,即Compound Index,指的是將多個(gè)鍵組合到一起創(chuàng)建索引,這樣可以加速匹配多個(gè)鍵的查詢。不妨通過一個(gè)簡單的示例理解復(fù)合索引。

students集合如下:

db.students.find().pretty()
{
    "_id" : ObjectId("5aa7390ca5be7272a99b042a"),
    "name" : "zhang",
    "age" : "15"
}
{
    "_id" : ObjectId("5aa7393ba5be7272a99b042b"),
    "name" : "wang",
    "age" : "15"
}
{
    "_id" : ObjectId("5aa7393ba5be7272a99b042c"),
    "name" : "zhang",
    "age" : "14"
}

在name和age兩個(gè)鍵分別創(chuàng)建了索引(_id自帶索引):

db.students.getIndexes()
[
    {
        "v" : 1,
        "key" : {
            "name" : 1
        },
        "name" : "name_1",
        "ns" : "test.students"
    },
    {
        "v" : 1,
        "key" : {
            "age" : 1
        },
        "name" : "age_1",
        "ns" : "test.students"
    }
]

當(dāng)進(jìn)行多鍵查詢時(shí),可以通過explian()分析執(zhí)行情況(結(jié)果僅保留winningPlan):

db.students.find({name:"zhang",age:"14"}).explain()
"winningPlan":
{
    "stage": "FETCH",
    "filter":
    {
        "name":
        {
            "$eq": "zhang"
        }
    },
    "inputStage":
    {
        "stage": "IXSCAN",
        "keyPattern":
        {
            "age": 1
        },
        "indexName": "age_1",
        "isMultiKey": false,
        "isUnique": false,
        "isSparse": false,
        "isPartial": false,
        "indexVersion": 1,
        "direction": "forward",
        "indexBounds":
        {
            "age": [
                "["14", "14"]"
            ]
        }
    }
}

由winningPlan可知,這個(gè)查詢依次分為IXSCANFETCH兩個(gè)階段。IXSCAN即索引掃描,使用的是age索引;FETCH即根據(jù)索引去查詢文檔,查詢的時(shí)候需要使用name進(jìn)行過濾。

為name和age創(chuàng)建復(fù)合索引:

db.students.createIndex({name:1,age:1})

db.students.getIndexes()
[
    {
        "v" : 1,
        "key" : {
            "name" : 1,
            "age" : 1
        },
        "name" : "name_1_age_1",
        "ns" : "test.students"
    }
]

有了復(fù)合索引之后,同一個(gè)查詢的執(zhí)行方式就不同了:

db.students.find({name:"zhang",age:"14"}).explain()
"winningPlan":
{
    "stage": "FETCH",
    "inputStage":
    {
        "stage": "IXSCAN",
        "keyPattern":
        {
            "name": 1,
            "age": 1
        },
        "indexName": "name_1_age_1",
        "isMultiKey": false,
        "isUnique": false,
        "isSparse": false,
        "isPartial": false,
        "indexVersion": 1,
        "direction": "forward",
        "indexBounds":
        {
            "name": [
                "["zhang", "zhang"]"
            ],
            "age": [
                "["14", "14"]"
            ]
        }
    }
}

由winningPlan可知,這個(gè)查詢的順序沒有變化,依次分為IXSCANFETCH兩個(gè)階段。但是,IXSCAN使用的是name與age的復(fù)合索引;FETCH即根據(jù)索引去查詢文檔,不需要過濾。

這個(gè)示例的數(shù)據(jù)量太小,并不能看出什么問題。但是實(shí)際上,當(dāng)數(shù)據(jù)量很大,IXSCAN返回的索引比較多時(shí),F(xiàn)ETCH時(shí)進(jìn)行過濾將非常耗時(shí)。接下來將介紹一個(gè)真實(shí)的案例。

定位MongoDB性能問題

隨著接收的錯(cuò)誤數(shù)據(jù)不斷增加,我們Fundebug已經(jīng)累計(jì)處理3.5億錯(cuò)誤事件,這給我們的服務(wù)不斷帶來性能方面的挑戰(zhàn),尤其對于MongoDB集群來說。

對于生產(chǎn)數(shù)據(jù)庫,配置profile,可以記錄MongoDB的性能數(shù)據(jù)。執(zhí)行以下命令,則所有超過1s的數(shù)據(jù)庫讀寫操作都會(huì)被記錄下來。

db.setProfilingLevel(1,1000)

查詢profile所記錄的數(shù)據(jù),會(huì)發(fā)現(xiàn)events集合的某個(gè)查詢非常慢:

db.system.profile.find().pretty()
{
    "op" : "command",
    "ns" : "fundebug.events",
    "command" : {
        "count" : "events",
        "query" : {
            "createAt" : {
                "$lt" : ISODate("2018-02-05T20:30:00.073Z")
            },
            "projectId" : ObjectId("58211791ea2640000c7a3fe6")
        }
    },
    "keyUpdates" : 0,
    "writeConflicts" : 0,
    "numYield" : 1414,
    "locks" : {
        "Global" : {
            "acquireCount" : {
                "r" : NumberLong(2830)
            }
        },
        "Database" : {
            "acquireCount" : {
                "r" : NumberLong(1415)
            }
        },
        "Collection" : {
            "acquireCount" : {
                "r" : NumberLong(1415)
            }
        }
    },
    "responseLength" : 62,
    "protocol" : "op_query",
    "millis" : 28521,
    "execStats" : {

    },
    "ts" : ISODate("2018-03-07T20:30:59.440Z"),
    "client" : "192.168.59.226",
    "allUsers" : [ ],
    "user" : ""
}

events集合中有數(shù)億個(gè)文檔,因此count操作比較慢也不算太意外。根據(jù)profile數(shù)據(jù),這個(gè)查詢耗時(shí)28.5s,時(shí)間長得有點(diǎn)離譜。另外,numYield高達(dá)1414,這應(yīng)該就是操作如此之慢的直接原因。根據(jù)MongoDB文檔,numYield的含義是這樣的:

The number of times the operation yielded to allow other operations to complete. Typically, operations yield when they need access to data that MongoDB has not yet fully read into memory. This allows other operations that have data in memory to complete while MongoDB reads in data for the yielding operation.

這就意味著大量時(shí)間消耗在讀取硬盤上,且讀了非常多次。可以推測,應(yīng)該是索引的問題導(dǎo)致的。

不妨使用explian()來分析一下這個(gè)查詢(僅保留executionStats):

db.events.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})
"executionStats":
{
    "executionSuccess": true,
    "nReturned": 20853,
    "executionTimeMillis": 28055,
    "totalKeysExamined": 28338,
    "totalDocsExamined": 28338,
    "executionStages":
    {
        "stage": "FETCH",
        "filter":
        {
            "createAt":
            {
                "$lt": ISODate("2018-02-05T20:30:00.073Z")
            }
        },
        "nReturned": 20853,
        "executionTimeMillisEstimate": 27815,
        "works": 28339,
        "advanced": 20853,
        "needTime": 7485,
        "needYield": 0,
        "saveState": 1387,
        "restoreState": 1387,
        "isEOF": 1,
        "invalidates": 0,
        "docsExamined": 28338,
        "alreadyHasObj": 0,
        "inputStage":
        {
            "stage": "IXSCAN",
            "nReturned": 28338,
            "executionTimeMillisEstimate": 30,
            "works": 28339,
            "advanced": 28338,
            "needTime": 0,
            "needYield": 0,
            "saveState": 1387,
            "restoreState": 1387,
            "isEOF": 1,
            "invalidates": 0,
            "keyPattern":
            {
                "projectId": 1
            },
            "indexName": "projectId_1",
            "isMultiKey": false,
            "isUnique": false,
            "isSparse": false,
            "isPartial": false,
            "indexVersion": 1,
            "direction": "forward",
            "indexBounds":
            {
                "projectId": [
                    "[ObjectId("58211791ea2640000c7a3fe6"), ObjectId("58211791ea2640000c7a3fe6")]"
                ]
            },
            "keysExamined": 28338,
            "dupsTested": 0,
            "dupsDropped": 0,
            "seenInvalidated": 0
        }
    }
}

可知,events集合并沒有為projectId與createAt建立復(fù)合索引,因此IXSCAN階段采用的是projectId索引,其nReturned為28338; FETCH階段需要根據(jù)createAt進(jìn)行過濾,其nReturned為20853,過濾掉了7485個(gè)文檔;另外,IXSCAN與FETCH階段的executionTimeMillisEstimate分別為30ms27815ms,因此基本上所有時(shí)間都消耗在了FETCH階段,這應(yīng)該是讀取硬盤導(dǎo)致的。

創(chuàng)建復(fù)合索引

沒有為projectId和createAt創(chuàng)建復(fù)合索引是個(gè)尷尬的錯(cuò)誤,趕緊補(bǔ)救一下:

db.events.createIndex({projectId:1,createTime:-1},{background: true})

在生產(chǎn)環(huán)境構(gòu)建索引這種事最好是晚上做,這個(gè)命令一共花了大概7個(gè)小時(shí)吧!background設(shè)為true,指的是不要阻塞數(shù)據(jù)庫的其他操作,保證數(shù)據(jù)庫的可用性。但是,這個(gè)命令會(huì)一直占用著終端,這時(shí)不能使用CTRL + C,否則會(huì)終止索引構(gòu)建過程。

復(fù)合索引創(chuàng)建成果之后,前文的查詢就快了很多(僅保留executionStats):

db.javascriptevents.explain("executionStats").count({"projectId" : ObjectId("58211791ea2640000c7a3fe6"),createAt:{"$lt" : ISODate("2018-02-05T20:30:00.073Z")}})
"executionStats":
{
    "executionSuccess": true,
    "nReturned": 0,
    "executionTimeMillis": 47,
    "totalKeysExamined": 20854,
    "totalDocsExamined": 0,
    "executionStages":
    {
        "stage": "COUNT",
        "nReturned": 0,
        "executionTimeMillisEstimate": 50,
        "works": 20854,
        "advanced": 0,
        "needTime": 20853,
        "needYield": 0,
        "saveState": 162,
        "restoreState": 162,
        "isEOF": 1,
        "invalidates": 0,
        "nCounted": 20853,
        "nSkipped": 0,
        "inputStage":
        {
            "stage": "COUNT_SCAN",
            "nReturned": 20853,
            "executionTimeMillisEstimate": 50,
            "works": 20854,
            "advanced": 20853,
            "needTime": 0,
            "needYield": 0,
            "saveState": 162,
            "restoreState": 162,
            "isEOF": 1,
            "invalidates": 0,
            "keysExamined": 20854,
            "keyPattern":
            {
                "projectId": 1,
                "createAt": -1
            },
            "indexName": "projectId_1_createTime_-1",
            "isMultiKey": false,
            "isUnique": false,
            "isSparse": false,
            "isPartial": false,
            "indexVersion": 1
        }
    }
}

可知,count操作使用了projectId和createAt的復(fù)合索引,因此非???,只花了46ms,性能提升了將近600倍?。?!對比使用復(fù)合索引前后的結(jié)果,發(fā)現(xiàn)totalDocsExamined從28338降到了0,表示使用復(fù)合索引之后不再需要去查詢文檔,只需要掃描索引就好了,這樣就不需要去訪問磁盤了,自然快了很多。

參考

MongoDB 復(fù)合索引

MongoDB文檔:Compound Indexes

版權(quán)聲明:
轉(zhuǎn)載時(shí)請注明作者Fundebug以及本文地址:
https://blog.fundebug.com/2018/03/15/mongdb_compound_index_detail/

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/19222.html

相關(guān)文章

  • MongoDB索引與優(yōu)化詳解

    摘要:全文索引在中每個(gè)集合只允許創(chuàng)建一個(gè)索引,因此不用擔(dān)心存在多個(gè)索引造成沖突的問題。全文索引創(chuàng)建全文索引創(chuàng)建方法與創(chuàng)建單鍵索引復(fù)合索引類似。 在MongoDB中通過建立索引可以進(jìn)行高效的查詢,如果沒有索引MongoDB將會(huì)掃描整個(gè)集合與查詢的條件進(jìn)行匹配,這對于性能會(huì)造成很大的消耗。技術(shù)博客: Node.js技術(shù)棧 快速導(dǎo)航 Mongodb索引類型 索引屬性 索引實(shí)例測試 索引(Ind...

    oujie 評論0 收藏0
  • MongoDB指南---11、使用復(fù)合索引、$操作符如何使用索引、索引對象和數(shù)組、索引基數(shù)

    摘要:操作符如何使用索引有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時(shí)能夠使用索引,但是通常它并不知道要如何使用索引。索引對象和數(shù)組允許深入文檔內(nèi)部,對嵌套字段和數(shù)組建立索引。 上一篇文章:MongoDB指南---10、索引、復(fù)合索引 簡介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引 1、使用復(fù)合索引 在多...

    saucxs 評論0 收藏0
  • MongoDB指南---11、使用復(fù)合索引、$操作符如何使用索引、索引對象和數(shù)組、索引基數(shù)

    摘要:操作符如何使用索引有一些查詢完全無法使用索引,也有一些查詢能夠比其他查詢更高效地使用索引。有時(shí)能夠使用索引,但是通常它并不知道要如何使用索引。索引對象和數(shù)組允許深入文檔內(nèi)部,對嵌套字段和數(shù)組建立索引。 上一篇文章:MongoDB指南---10、索引、復(fù)合索引 簡介下一篇文章:MongoDB指南---12、使用explain()和hint()、何時(shí)不應(yīng)該使用索引 1、使用復(fù)合索引 在多...

    tomlingtm 評論0 收藏0
  • 練習(xí) MongoDB 操作 —— 索引篇(二)

    摘要:所以,如果你很少對集合進(jìn)行讀取操作,建議不使用索引內(nèi)存使用由于索引是存儲(chǔ)在內(nèi)存中你應(yīng)該確保該索引的大小不超過內(nèi)存的限制。如果索引的大小大于內(nèi)存的限制,會(huì)刪除一些索引,這將導(dǎo)致性能下降。 本文圍繞索引、游標(biāo)兩部分進(jìn)行探索,對MongoDB數(shù)據(jù)庫的索引部分有一個(gè)大概的了解; 索引 索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取數(shù)據(jù)時(shí)必須掃描集合中的每個(gè)文件并選取那些符...

    luqiuwen 評論0 收藏0
  • mongodb索引

    摘要:但是需要手動(dòng)創(chuàng)建創(chuàng)建索引,索引可以重復(fù)創(chuàng)建,若創(chuàng)建已經(jīng)存在的索引,則會(huì)直接返回成功。單鍵索引值為一個(gè)單一的值,如字符串,數(shù)字或日期。多鍵索引值具有多個(gè)記錄,如數(shù)組。過期索引不能是復(fù)合索引。 索引的概念 索引是一種單獨(dú)的、物理的對數(shù)據(jù)庫表中一列或多列的值進(jìn)行排序的一種存儲(chǔ)結(jié)構(gòu),通過索引可以快速找到我們查詢的數(shù)據(jù)。提高查詢效率 mongodb索引種類 _id索引 單鍵索引 多鍵索引 復(fù)合...

    FWHeart 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<