索引定义
对文档部分内容进行排序的数据结构,可以加快文档查询和排序的速度。
MongoDB的索引采用B-tree的数据结构,有关这种数据结构以及和B+tree的区别另开文章讲述。
(—这里将来会有个链接—)
复合索引
accounts 集合文档:
{ name : "alice", currency : "cny", balance : 100 },
{ name : "bob", currency : "cny", balance : 50 },
{ name : "alice", currency : "usd", balance : 200 },
{ name : "billy", currency : "usd", balance : 20 }
当建立 name - currency - balance 的复合索引时
db.accounts.createIndex({name:1,currency:1,balance:1});
会生成类似下列的结构
alice | alice | billy | bob |
---|---|---|---|
cny | usd | usd | cny |
100 | 200 | 20 | 50 |
为了便于说明,以(A, B, C)来表示这个复合索引,同Mysql一样,MongoDB的复合索引同样遵循前缀原则,即{A}、{A, B}、{A, C}、{A, B, C}这四种查询方式。 | |||
可以通过explain命令来验证是否使用了索引查询: | |||
{A} |
db.accounts.find({name:"alice"}).explain();
//只截取部分返回结果
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"name": 1,
"currency": 1,
"balance": 1
},
"indexName": "name_1_currency_1_balance_1",
{A, B}
db.accounts.find({name:"alice",currency:"cny"}).explain();
//只截取部分返回结果
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"name": 1,
"currency": 1,
"balance": 1
},
"indexName": "name_1_currency_1_balance_1",
{A, C}
db.accounts.find({name:"alice",balance:100}).explain();
//只截取部分返回结果
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"name": 1,
"currency": 1,
"balance": 1
},
"indexName": "name_1_currency_1_balance_1",
{A, B, C}
db.accounts.find({name:"alice",currency:"cny",balance:100}).explain();
//只截取部分返回结果
"winningPlan": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"name": 1,
"currency": 1,
"balance": 1
},
"indexName": "name_1_currency_1_balance_1",
再看一下未应用到索引时的结果,以{B, C}查询方式为例
db.accounts.find({currency:"cny",balance:100}).explain();
//只截取部分返回结果
"winningPlan": {
"stage": "COLLSCAN",
"filter": {
"$and": [
{
"balance": {
"$eq": 100
}
},
{
"currency": {
"$eq": "cny"
}
}
]
},
"direction": "forward"
},
多键索引
集合文档
{ name : "alice", currency : [ "CNY", "USD" ] },
{ name : "bob", currency : [ "USD", "GBP" ] }
对currency这个数组字段建立索引时,MongoDB会为集合中的每一个元素创建一个索引,类似于:
//建立索引语句
db.accounts.createIndex({currency:1})
//索引结构参考
"CNY" -> { "alice" }
"GBP" -> { "bob" }
"USD" -> { "alice" }
"USD" -> { "bob" }
删除索引
db.accounts.dropIndex();
如果需要更改已存在的索引,必须先删除索引后再创建,否则新创建的索引将不会包含已存在的文档。
先查看下已存在的索引:
db.accounts.getIndexes();
//结果
[
{
"v": NumberInt("2"),
"key": {
"_id": NumberInt("1")
},
"name": "_id_"
},
{
"v": NumberInt("2"),
"key": {
"name": 1,
"currency": 1,
"balance": 1
},
"name": "name_1_currency_1_balance_1"
}
]
可以用下列两种方式删除:
//使用key和排序方式
db.accounts.dropIndex({"name": 1,"currency": 1,"balance": 1});
//或者使用索引名称
db.accounts.dropIndex("name_1_currency_1_balance_1");
索引特性
唯一性
db.accounts.createIndex({currency:1},{unique:true});
如果已有文档中的某个字段出现了重复值,就不可以在这个字段上创建唯一性索引,否则会提示重复值错误。
如果新增的文档不包含唯一性索引字段,只有第一篇缺失该字段的文档可以被写入,索引中该字段的键值被默认为null。如果想要插入一篇以上这样的文档,就需要借助稀疏性来实现。
复合键索引也可以具有唯一性,此时,不同文档之间,其所包含的复核健字段值的组合,不可以重复。
多键索引同样可以具有唯一性,情况同单键唯一性索引一致,上演示栗子:
//已存在文档
{name:"lillian",contact:["1","2"]}
//创建唯一性索引
db.accounts.createIndex({contact:1},{unique:true});
//新增一篇文档
db.accounts.insertOne({name:"elijah",contact:["1"]});
db.accounts.insertOne({name:"elijah",contact:["2"]});
db.accounts.insertOne({name:"elijah",contact:["1","2"]});
db.accounts.insertOne({name:"elijah",contact:["1","3"]});
//以上四条语句都会返回报错
[Error] index 0: 11000 - E11000 duplicate key error collection: temp.accounts index: contact_1 dup key: { contact: "1" }
其实,只要回忆一下多键索引的结构就可以理解为何会这样了。
稀疏性
db.accounts.createIndex({currency:1},{sparse:true});
通过设置sparse为true,会将仅包含索引键字段的文档加入到索引中,即便该字段的值为null也不会加入索引,这样可以节省索引所占用的空间。
如果一个索引同时具备唯一性和稀疏性,就可以保存多篇缺失索引字段的文档了。
复合健索引也可以具备稀疏性,只有复合键所包含的所有字段都缺失的情况下,才不会被写入索引中。
生存时间特性
db.accounts.createIndex({lastAccess:1},{expireAfterSeconds:60});
针对日期字段,或者包含日期元素的数组字段,可以为这样的字段创建具有生存时间特性的索引,从而自动删除字段值超过生存时间的文档。
//原文档
{name:"charlie",lastAccess:ISODate("2021-02-09T10:01:02.101Z")}
//创建索引
db.accounts.createIndex({lastAccess:1},{expireAfterSeconds:20});
根据上面的语句,当lastAccess字段存储的时间对应的时间戳+20秒小于当前时间,数据库后台的进程就会删除这个文档。
复合键索引不具备生存时间特性。
当索引键是包含日期元素的数组字段时,数组中最小的日期(即时间戳最小)将被用来计算文档是否已经过期。
由于数据库是使用一个后台进程进行监测和删除操作,所以存在一定的延时性。