MongoDB 索引

索引定义

对文档部分内容进行排序的数据结构,可以加快文档查询和排序的速度。
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});

会生成类似下列的结构

alicealicebillybob
cnyusdusdcny
1002002050
为了便于说明,以(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秒小于当前时间,数据库后台的进程就会删除这个文档。
复合键索引不具备生存时间特性。
当索引键是包含日期元素的数组字段时,数组中最小的日期(即时间戳最小)将被用来计算文档是否已经过期。
由于数据库是使用一个后台进程进行监测和删除操作,所以存在一定的延时性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值