本篇文章带你了解MongoDB,介绍一下MongoDB中丰富的索引类型,希望对大家有所帮助!

MongoDB
的索引和MySql
的索引的作用和优化要遵循的原则基本相似,MySql
索引类型基本可以区分为:
- 单键索引 - 联合索引
- 主键索引(聚簇索引) - 非主键索引(非聚簇索引)
在MongoDB
中除了这些基础的分类之外,还有一些特殊的索引类型,如: 数组索引 | 稀疏索引 | 地理空间索引 | TTL索引等.
为了下面方便测试我们使用脚本插入以下数据
for(var i = 0;i < 100000;i++){
db.users.insertOne({
username: "user"+i,
age: Math.random() * 100,
sex: i % 2,
phone: 18468150001+i
});
}
单键索引
单键索引即索引的字段只有一个,是最基础的索引方式.
在集合中使用username
字段,创建一个单键索引,MongoDB
会自动将这个索引命名为username_1
db.users.createIndex({username:1})
'username_1'
在创建索引后查看一下使用username
字段的查询计划,stage
为IXSCAN
代表使用使用了索引扫描
db.users.find({username:"user40001"}).explain()
{
queryPlanner:
{
winningPlan:
{
......
stage: 'FETCH',
inputStage:
{
stage: 'IXSCAN',
keyPattern: { username: 1 },
indexName: 'username_1',
......
}
}
rejectedPlans: [] ,
},
......
ok: 1
}
在索引优化的原则当中,有很重要的原则就是索引要建立在基数高的的字段上,所谓基数就是一个字段上不重复数值的个数,即我们在创建users
集合时年龄出现的数值是0-99
那么age
这个字段将会有100个不重复的数值,即age
字段的基数为100,而sex
这个字段只会出现0 | 1
这个两个值,即sex
字段的基础是2,这是一个相当低的基数,在这种情况下,索引的效率并不高并且会导致索引失效.
下面就船舰一个sex
字段索引,来查询执行计划会发现,查询时是走的全表扫描,而没有走相关索引.
db.users.createIndex({sex:1})
'sex_1'
db.users.find({sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: 'COLLSCAN',
filter: { sex: { '$eq': 1 } },
direction: 'forward'
},
rejectedPlans: []
},
......
ok: 1
}
联合索引
联合索引即索引上会有多个字段,下面使用age
和sex
两个字段创建一个索引
db.users.createIndex({age:1,sex:1})
'age_1_sex_1'
然后我们使用这两个字段进行一次查询,查看执行计划,顺利地走了这条索引
db.users.find({age:23,sex:1}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: 'FETCH',
inputStage:
{
stage: 'IXSCAN',
keyPattern: { age: 1, sex: 1 },
indexName: 'age_1_sex_1',
.......
indexBounds: { age: [ '[23, 23]' ], sex: [ '[1, 1]' ] }
}
},
rejectedPlans: [],
},
......
ok: 1
}
数组索引
数组索引就是对数组字段创建索引,也叫做多值索引,下面为了测试将users
集合中的数据增加一部分数组字段.
db.users.updateOne({username:"user1"},{$set:{hobby:["唱歌","篮球","rap"]}})
......
创建数组索引并进行查看其执行计划,注意isMultiKey: true
表示使用的索引是多值索引.
db.users.createIndex({hobby:1})
'hobby_1'
db.users.find({hobby:{$elemMatch:{$eq:"钓鱼"}}}).explain()
{
queryPlanner:
{
......
winningPlan:
{
stage: 'FETCH',
filter: { hobby: { '$elemMatch': { '$eq': '钓鱼' } } },
inputStage:
{
stage: 'IXSCAN',
keyPattern: { hobby: 1 },
indexName: 'hobby_1',
isMultiKey: true,
multiKeyPaths: { hobby: [ 'hobby' ] },
......
indexBounds: { hobby: [ '["钓鱼", "钓鱼"]' ] } }
},
rejectedPlans: []
},
......
ok: 1
}
数组索引相比于其它索引来说索引条目和体积必然呈倍数增加,例如平均每个文档的hobby
数组的size
为10,那么这个集合的hobby
数组索引的条目数量将是普通索引的10倍.
联合数组索引
联合数组索引就是含有数组字段的联合索引,这种索引不支持一个索引中含有多个数组字段,即一个索引中最多能有一个数组字段,这是为了避免索引条目爆炸式增长,假设一个索引中有两个数组字段,那么这个索引条目的数量将是普通索引的n*m倍
地理空间索引
在原先的users
集合上,增加一些地理信息
for(var i = 0;i < 100000;i++){
db.users.updateOne(
{username:"user"+i},
{
$set:{
location:{
type: "Point",
coordinates: [100+Math.random() * 4,40+Math.random() * 3]
}
}
});
}
创建一个二维空间索引
db.users.createIndex({location:"2dsphere"})
'location_2dsphere'
//查询500米内的人
db.users.find({
location:{
$near:{
$geometry:{type:"Point",coordinates:[102,41.5]},
$maxDistance:500
}
}
})
地理空间索引的type
有很多包含Ponit(点)
| LineString(线)
| Polygon(多边形)
等
TTL索引
TTL的全拼是time to live
,主要是用于过期数据自动删除,使用这种索引需要在文档中声明一个时间类型的字段,然后为这个字段创建TTL索引的时候还需要设置一个expireAfterSeconds
过期时间单位为秒,创建完成后MongoDB
会定期对集合中的数据进行检查,当出现: