持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情
索引前缀
索引前缀是复合索引的从左侧开始的一个子集。假如我们有以下复合索引:
{ "item": 1, "location": 1, "stock": 1 }
则该索引具有以下索引前缀
{ "item": 1}
{ "item": 1, "location": 1}
对于复合索引,MongoDB可以使用复合索引的前缀来查询。因此,可以查询以下字段:
itemitem和locationitem、location和stock
还可以支持对item和stock 字段的查询,因为item 字段是索引前缀。但是这样查询肯定不如只有item和stock字段对应的复合索引。**索引会按照字段顺序解析,如果查询省略了特定的索引前缀,则无法使用该前缀后面的任何索引字段。**由于中间省略了location,不能使用stock字段,只支持item字段。
还不支持以下字段查询:
locationstocklocation和stock
前面对 person.json已经创建了last_name和first_name的复合索引。因为我前面还创建了last_name 的单键索引,需要测试,我们先删除。再执行以下查询
db.people.dropIndex("last_name_1")
exp = db.people.explain("executionStats")
exp.find( { "last_name" : "Solomon" } )
{
executionStats: {
executionSuccess: true,
nReturned: 22,
executionTimeMillis: 0,
totalKeysExamined: 22,
totalDocsExamined: 22,
executionStages: {
stage: 'FETCH',
nReturned: 22,
inputStage: {
stage: 'IXSCAN',
nReturned: 22,
indexName: 'last_name_1_first_name_1'
}
}
}
}
可以看到扫描了22个索引键,22个文档,返回了22个文档,非常好比例。没有查看多余的索引键,执行时间页很快。
但是我们假如查询first_name ,而不是查询last_name
exp.find( { "first_name" : "Sonia" } )
{
executionStats: {
executionSuccess: true,
nReturned: 8,
executionTimeMillis: 29,
totalKeysExamined: 0,
totalDocsExamined: 50474,
executionStages: {
stage: 'COLLSCAN',
filter: { first_name: { '$eq': 'Sonia' } },
}
}
}
可以看到使用了全表扫描,没有使用索引键,而且也花了很长的时间。我们不能使用这个复合索引的原因是,我们没有使用索引前缀。
我们也可以看看查询结果发现last_name是有序的,first_name可以在任意位置。first_name顺序是相对于last_name的,但是first_name本身,如果是我们要查询的,可能出现在任意位置。所以这个复合索引相对于这条查询时无效的,依然需要去进行全表扫描。
如果我们有一个复合索引,必须使用前缀才可以,否则不会使用当前索引。我们在构建复合索引的时候需要考虑清楚。如果我们有两个查询,其中一个使用作为另一个子集的字段,应该构建一个索引,其中一个查询使用索引前缀,另一个使用索引的所有字段。