MongoDB数据库索引基础知识与实战技巧
本文內(nèi)容源自Kyle Banker 的MongoDB In Action一書。主要描述了MongoDB索引相關(guān)的一些基礎(chǔ)知識(shí)和使用技巧。
索引類型
雖然MongoDB的索引在存儲(chǔ)結(jié)構(gòu)上都是一樣的,但是根據(jù)不同的應(yīng)用層需求,還是分成了唯一索引(unique)、稀疏索引(sparse)、多值索引(multikey)等幾種類型。
1.唯一索引
唯一索引在創(chuàng)建時(shí)加上unique:true 的選項(xiàng)即可,創(chuàng)建命令如下:
db.users.ensureIndex({username: 1}, {unique: true})
上面的唯一索引創(chuàng)建后,如果insert一條username已經(jīng)存在的數(shù)據(jù),則會(huì)報(bào)如下的錯(cuò)誤:
E11000 duplicate key error index: gardening.users.$username_1 dup key: { : "kbanker" }
如果你在一個(gè)已有數(shù)據(jù)的collection上創(chuàng)建唯一索引,若唯一索引對(duì)應(yīng)的字段原來就有重復(fù)的數(shù)據(jù)項(xiàng),那么創(chuàng)建會(huì)失敗,我們需要加上一個(gè)dropDups的選項(xiàng)來強(qiáng)制將重復(fù)的項(xiàng)刪除掉,命令如下例:
db.users.ensureIndex({username: 1}, {unique: true, dropDups: true})
2.松散索引
如果你的數(shù)據(jù)中一些行中沒有某個(gè)字段或字段值為null,那么如果在這個(gè)字段上建立普通索引,那么無此字段或值null的行也會(huì)參與到索引結(jié)構(gòu)中,占用相應(yīng)的空間。如果我們不希望這些值為空的行參與到我們的索引中,這時(shí)候可以采用松散索引,松散索引只會(huì)讓指定字段不為空的行參與到索引創(chuàng)建中來。創(chuàng)建一個(gè)松散索引可以用下面的命令:
db.reviews.ensureIndex({user_id: 1}, {sparse: true})
3.多值索引
MongoDB可以對(duì)一個(gè)array類型創(chuàng)建索引,比如像下面的結(jié)構(gòu),MongoDB可以在tags字段上創(chuàng)建索引:
{ name: "Wheelbarrow",
tags: ["tools", "gardening", "soil"]
}
在生成索引時(shí),會(huì)為tags中的三個(gè)值分別生成三個(gè)索引元素,索引中tools,gardening,soil三個(gè)值都會(huì)指向這同一行數(shù)據(jù)。相當(dāng)于分裂成了三個(gè)獨(dú)立的索引項(xiàng)。
索引管理
1.索引的創(chuàng)建和刪除
創(chuàng)建和刪除索引的方法有很多種,下面兩個(gè)是比較原始的方法,通過對(duì)system.indexes這個(gè)collection進(jìn)行相應(yīng)的寫操作來完成索引的創(chuàng)建:
spec = {ns: "green.users", key: {‘a(chǎn)ddresses.zip’: 1}, name: ‘zip’}
db.system.indexes.insert(spec, true)
上面命令往system.indexes中寫入一條記錄來創(chuàng)建索引,這條記錄包含了要在上面創(chuàng)建索引的collection的名字空間,索引的信息,以及索引的名稱。
創(chuàng)建完成后,我們可以通過下面命令找到我們創(chuàng)建的索引:
db.system.indexes.find()
{ "_id" : ObjectId("4d2205c4051f853d46447e95"), "ns" : "green.users",
"key" : { "addresses.zip" : 1 }, "name" : "zip", "v" : 0 }
要?jiǎng)h除一個(gè)已創(chuàng)建的索引,我們可以使用下面的命令來實(shí)現(xiàn):
use green
db.runCommand({deleteIndexes: "users", index: "zip"})
2.創(chuàng)建索引命令
實(shí)際上創(chuàng)建索引還有更方便的命令,那就是ensureIndex,比如我們創(chuàng)建一個(gè)open和close兩個(gè)字段的聯(lián)合索引,就可以用下面的命令:
db.values.ensureIndex({open: 1, close: 1})
這個(gè)命令會(huì)觸發(fā)索引創(chuàng)建的兩個(gè)過程,一個(gè)是將相應(yīng)的字段排序,因?yàn)樗饕前碆+樹來組織的,要構(gòu)建樹,將數(shù)據(jù)進(jìn)行排序后能夠提高插入B+樹的效率(第二個(gè)過程的效率),在日志中,你能看到和下面類似的輸出:
Tue Jan 4 09:58:17 [conn1] building new index on { open: 1.0, close: 1.0 } for stocks.values
1000000/4308303 23%
2000000/4308303 46%
3000000/4308303 69%
4000000/4308303 92%
Tue Jan 4 09:59:13 [conn1] external sort used : 5 files in 55 secs
第二個(gè)過程是將排序好的數(shù)據(jù)插入到索引結(jié)構(gòu)中,構(gòu)成可用的索引:
1200300/4308303 27%
2227900/4308303 51%
2837100/4308303 65%
3278100/4308303 76%
3783300/4308303 87%
4075500/4308303 94%
Tue Jan 4 10:00:16 [conn1] done building bottom layer, going to commit
Tue Jan 4 10:00:16 [conn1] done for 4308303 records 118.942secs
Tue Jan 4 10:00:16 [conn1] insert stocks.system.indexes 118942ms
除了日志中的輸出外,你還可以通過在終端執(zhí)行currentOp命令來獲取當(dāng)前操作線程的相關(guān)信息,如下例:
> db.currentOp()
{
"inprog" : [
{
"opid" : 58,
"active" : true,
"lockType" : "write",
"waitingForLock" : false,
"secs_running" : 55,
"op" : "insert",
"ns" : "stocks.system.indexes",
"query" : {
},
"client" : "127.0.0.1:53421",
"desc" : "conn",
"msg" : "index: (1/3) external sort 3999999/4308303 92%"
}
]
}
最后一部分就是一個(gè)索引構(gòu)建過程,目前正在執(zhí)行排序過程,執(zhí)行到92%。
3.在后臺(tái)創(chuàng)建索引
創(chuàng)建索引會(huì)對(duì)數(shù)據(jù)庫添加寫鎖,如果數(shù)據(jù)集比如大,會(huì)將線上讀寫數(shù)據(jù)庫的操作掛起,以等待索引創(chuàng)建結(jié)束。這影響了數(shù)據(jù)庫的正常服務(wù),我們可以通過在創(chuàng)建索引時(shí)加background:true 的選項(xiàng),讓創(chuàng)建工作在后臺(tái)執(zhí)行,這時(shí)候創(chuàng)建索引還是需要加寫鎖,但是這個(gè)寫鎖不會(huì)直接獨(dú)占到索引創(chuàng)建完成,而是會(huì)暫停為其它讀寫操作讓路,不至于造成嚴(yán)重的性能影響。具體方法:
db.values.ensureIndex({open: 1, close: 1}, {background: true})
4.離線創(chuàng)建索引
無論如何,索引的創(chuàng)建都會(huì)給數(shù)據(jù)庫造成一定的壓力,從而影響線上服務(wù)。如果希望創(chuàng)建索引的過程完全不影響線上服務(wù),我們可以通過將replica sets中的節(jié)點(diǎn)先從集群中剝離,在這個(gè)節(jié)點(diǎn)上添加相應(yīng)的索引,等索引添加完畢后再將其添加到replica sets中。這只需要保證一個(gè)條件,就是創(chuàng)建索引的時(shí)間不能長(zhǎng)于oplog能夠保存日志的時(shí)間,否則創(chuàng)建完后節(jié)點(diǎn)再上線發(fā)現(xiàn)再也無法追上primary了,這時(shí)會(huì)進(jìn)行resync操作。
索引備份
我們知道,無論是使用mongodump還是mongoexport命令,都只是對(duì)數(shù)據(jù)進(jìn)行備份,無法備份索引。我們?cè)诨謴?fù)的時(shí)候,還是需要等待漫長(zhǎng)的索引創(chuàng)建過程。所以,如果你希望備份的時(shí)候帶上索引,那么最好采用備份數(shù)據(jù)文件的方式。
索引壓縮
索引在使用一段時(shí)間后,經(jīng)歷增刪改等操作,會(huì)變得比較松散,從而戰(zhàn)用不必要的空間,我們可以通過reindex命令,重新組織索引,讓索引的空間占用變得更小。
?
轉(zhuǎn)載于:https://blog.51cto.com/wws5201985/773843
總結(jié)
以上是生活随笔為你收集整理的MongoDB数据库索引基础知识与实战技巧的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019花呗有3天宽限期吗
- 下一篇: [轉]MS SQL Server启用AW