持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
由于MongoDB
灵活的数据模型,我们能够将数组嵌入到我们的文档中。当我们给一个数组创建索引时,就是多键索引。之所以叫多键索引,是因为MongoDB
会给数组中的每一个元素都创建一个索引键。这些多键索引支持对数组字段的高效查询。
多键索引可以在字符串、数字和内嵌文档的数组上创建。
创建多键索引
db.<collection>.createIndex( { <field>: < 1 or -1 > } )
MongoDB 3.4
开始,对于3.4
及更高版本创建多键索引,会跟踪那些字段导致索引成为多键索引。因此,我们不需要明确指定多键类型。
使用
插入以下下数据
use test
db.products.insert({
productName: "MongoDB Short Sleeve T-Shirt",
categories: ["T-Shirts", "Clothing", "Apparel"],
stock: { size: "L", color: "green", quantity: 100 }
});
例如在上面这个文档中,我们要为categories
创建索引,那么服务器将为T-Shirt
、Clothing
和Apparel
创建3个索引键,每个子文档一个。
对于每个索引文档,我们最多可以有一个索引字段,其值为数组。在上面的文档中,我们不能同时为categories
和stock.quantity
创建索引。假如要创建复合索引,那么将为他们创建categories
和stock.quantity
数组长度之间的笛卡尔积,会产生大量的索引键,对查询性能影响很大。
创建多键索引要注意,因为我们要确保我们的数组不会变的太大。因为这样做会影响索引的大小,可能会导致将索引加载到内存中,将数据查询出来的问题。多键索引不支持覆盖查询。
我们给stock.quantity
创建索引,然后在查询一下
db.products.createIndex({ "stock.quantity": 1})
var exp = db.products.explain()
exp.find({ "stock.quantity": 100 })
{
queryPlanner: {
namespace: 'test.products',
indexFilterSet: false,
parsedQuery: { 'stock.quantity': { '$eq': 100 } },
queryHash: 'AFCEECBB',
planCacheKey: '4A0F65D8',
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
winningPlan: {
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: { 'stock.quantity': 1 },
indexName: 'stock.quantity_1',
//是否使用多键索引
isMultiKey: false,
multiKeyPaths: { 'stock.quantity': [] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { 'stock.quantity': [ '[100, 100]' ] }
}
},
rejectedPlans: []
}
}
我们可以看到使用了索引扫描,但是没有使用成功(isMultiKey = false
),多键索引时假的,这是有道理的,因为我们没有在数组字段上创建索引,再插入一条记录,stock
为内嵌数组。
db.products.insert({
productName: "MongoDB Long Sleeve T-Shirt",
categories: ["T-Shirts", "Clothing", "Apparel"],
stock: [
{ size: "S", color: "red", quantity: 25 },
{ size: "S", color: "blue", quantity: 10 },
{ size: "M", color: "blue", quantity: 50 }
]
});
exp.find({ "stock.quantity": 100 })
{
queryPlanner: {
namespace: 'test.products',
indexFilterSet: false,
parsedQuery: { 'stock.quantity': { '$eq': 100 } },
queryHash: 'AFCEECBB',
planCacheKey: '4A0F65D8',
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
winningPlan: {
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: { 'stock.quantity': 1 },
indexName: 'stock.quantity_1',
isMultiKey: true,
multiKeyPaths: { 'stock.quantity': [ 'stock' ] },
isUnique: false,
isSparse: false,
isPartial: false,
indexVersion: 2,
direction: 'forward',
indexBounds: { 'stock.quantity': [ '[100, 100]' ] }
}
},
rejectedPlans: []
}
}
现在我们可以看到依然是索引扫描,但是使用了多键索引(isMultiKey = true
),因为MongoDB
将文档插入到该索引字段位于数组中的位置时才识别索引是多键的。
如果我们尝试创建两个字段都是数组的索引
db.products.createIndex({ "stock.quantity": 1, "categories": 1})
MongoServerError: Index build failed: 8cc81f7e-30fb-41f8-aa6a-f11c5c979e80: Collection test.products ( bde69fd0-a25b-418f-9da1-7af93c63442b ) :: caused by :: cannot index parallel arrays [categories] [stock]
但是我们仍然可以给stock.quantity
和productName
创建复合多键索引。也可以为stock.quantity
和stock.size
创建索引,因为他们是同一个数组中的两个字段。
db.products.createIndex({ "stock.quantity": 1, "productName": `})
stock.quantity_1_productName_1
db.products.createIndex({ "stock.quantity": 1, "stock.size": 1})
stock.quantity_1_stock.size_1